@hasna/skills 0.10.53 → 0.10.55
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/README.md +3 -3
- package/bin/index.js +50 -9
- package/bin/mcp.js +10 -4
- package/bin/migrate.js +1 -1
- package/bin/server.js +1 -1
- package/bin/worker.js +1 -1
- package/dist/index.js +34 -6
- package/dist/lib/session-reconciliation.d.ts +23 -0
- package/dist/sdk/index.d.ts +1 -1
- package/dist/sdk/index.js +34 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -643,9 +643,9 @@ skills hook native-catalog --cwd /absolute/project --output /private/catalog.jso
|
|
|
643
643
|
skills hook install --agent codex --codex-native-catalog /private/catalog.json --json
|
|
644
644
|
```
|
|
645
645
|
|
|
646
|
-
Codex 0.159.2, 0.160.0
|
|
646
|
+
Codex 0.159.2, 0.160.0, 0.160.1 and 0.161.0 have measured qualified-name catalog support.
|
|
647
647
|
Native transport, capture and review use one capability registry of exact
|
|
648
|
-
releases. Unmeasured releases, including 0.160.2 and 0.161.
|
|
648
|
+
releases. Unmeasured releases, including 0.160.2 and 0.161.1, refuse until the
|
|
649
649
|
native catalog/name-denial and hook-dispatch acceptance tests pass; an RPC
|
|
650
650
|
handshake alone does not establish those semantics. Admitting a release does not
|
|
651
651
|
admit its corpus: the transport still requires the existing native corpus
|
|
@@ -697,7 +697,7 @@ different extant, witnessed user version. Individual missing files, current
|
|
|
697
697
|
registered roots, aliases and registration changes still refuse; historical
|
|
698
698
|
source hashes and typed settings witnesses remain intact.
|
|
699
699
|
|
|
700
|
-
For a Codex 0.160.0
|
|
700
|
+
For a Codex 0.160.0, 0.160.1 or 0.161.0 remote refresh omitted by an existing qualified-name disable,
|
|
701
701
|
a fresh native catalog and discovery review can plan the new exact path disable
|
|
702
702
|
in the same transaction. The remote installation receipt, namespace and capability
|
|
703
703
|
controls must match; conflicting enable rules, unknown names and ambiguous
|
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.
|
|
36891
|
+
version: "0.10.55",
|
|
36892
36892
|
description: "Skills library for AI coding agents",
|
|
36893
36893
|
type: "module",
|
|
36894
36894
|
bin: {
|
|
@@ -40291,7 +40291,8 @@ var init_codex_native_compatibility = __esm(() => {
|
|
|
40291
40291
|
"codex-cli 0.159.0": ["hooks"],
|
|
40292
40292
|
"codex-cli 0.159.2": ["hooks", "qualified-skill-catalog"],
|
|
40293
40293
|
"codex-cli 0.160.0": ["hooks", "qualified-skill-catalog", "installed-plugin-review"],
|
|
40294
|
-
"codex-cli 0.160.1": ["hooks", "qualified-skill-catalog", "installed-plugin-review"]
|
|
40294
|
+
"codex-cli 0.160.1": ["hooks", "qualified-skill-catalog", "installed-plugin-review"],
|
|
40295
|
+
"codex-cli 0.161.0": ["hooks", "qualified-skill-catalog", "installed-plugin-review"]
|
|
40295
40296
|
};
|
|
40296
40297
|
SUPPORTED_CODEX_HOOK_VERSIONS = Object.freeze(Object.keys(MEASURED_CODEX_CAPABILITIES).filter((version) => supportsCodexNativeCapability(version, "hooks")));
|
|
40297
40298
|
});
|
|
@@ -59505,12 +59506,17 @@ function captureRetiredCodexDiscovery(binding, controls, rules) {
|
|
|
59505
59506
|
return { ...current, codexRetiredMaterializations: previous };
|
|
59506
59507
|
return { ...current, codexRetiredMaterializations: { roots, parents: parents3, directories: captureDiscoveryDirectoryProjection(directories.map((item) => item.path), roots) } };
|
|
59507
59508
|
}
|
|
59509
|
+
function retainableAbsentSource(source) {
|
|
59510
|
+
if (source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined)
|
|
59511
|
+
return false;
|
|
59512
|
+
return [undefined, "bytes"].includes(source.hashMode) || source.hashMode === "claude-plugin-manifest-v1" && source.sha256 !== null && source.path.endsWith(join37(sep8, ".claude-plugin", "plugin.json"));
|
|
59513
|
+
}
|
|
59508
59514
|
function absentRetiredClaudeRoots(binding) {
|
|
59509
59515
|
if (binding.agent !== "claude" || binding.method !== "reviewed")
|
|
59510
59516
|
return [];
|
|
59511
59517
|
const result2 = new Set;
|
|
59512
59518
|
for (const source of binding.sources) {
|
|
59513
|
-
if (
|
|
59519
|
+
if (!retainableAbsentSource(source) || source.sha256 === null)
|
|
59514
59520
|
continue;
|
|
59515
59521
|
const marker = sep8 + ".claude" + sep8 + "plugins" + sep8 + "cache" + sep8, at2 = source.path.indexOf(marker);
|
|
59516
59522
|
if (at2 < 0 || source.path.indexOf(marker, at2 + 1) >= 0)
|
|
@@ -59622,7 +59628,7 @@ function verifyAgentDiscovery(binding, codexRecovery) {
|
|
|
59622
59628
|
safe4(source.path);
|
|
59623
59629
|
continue;
|
|
59624
59630
|
}
|
|
59625
|
-
if (absentRoots.some((root2) => source.path.startsWith(root2 + sep8)) &&
|
|
59631
|
+
if (absentRoots.some((root2) => source.path.startsWith(root2 + sep8)) && retainableAbsentSource(source)) {
|
|
59626
59632
|
safe4(source.path);
|
|
59627
59633
|
continue;
|
|
59628
59634
|
}
|
|
@@ -59678,7 +59684,7 @@ function rebindAgentDiscovery(binding, changes) {
|
|
|
59678
59684
|
const installationRoots = codexInstallationRoots(binding), absentRoots = [...absentCodexSkillRoots(binding), ...absentRetiredClaudeRoots(binding), ...retiredCodexRoots(binding).filter((root2) => !lstatSync25(root2, { throwIfNoEntry: false }))];
|
|
59679
59685
|
const rebound = { ...binding, sources: binding.sources.map((source) => {
|
|
59680
59686
|
assertMarketplaceBinding(binding, source);
|
|
59681
|
-
if (!changes.has(source.path) && (installationRoots.some((root2) => source.path.startsWith(root2 + sep8)) || absentRoots.some((root2) => source.path.startsWith(root2 + sep8)) &&
|
|
59687
|
+
if (!changes.has(source.path) && (installationRoots.some((root2) => source.path.startsWith(root2 + sep8)) || absentRoots.some((root2) => source.path.startsWith(root2 + sep8)) && retainableAbsentSource(source)))
|
|
59682
59688
|
return source;
|
|
59683
59689
|
const sha2563 = projected(source, changes, budget2);
|
|
59684
59690
|
if (!changes.has(source.path) && sha2563 !== source.sha256)
|
|
@@ -86064,8 +86070,12 @@ function assertPlanWindow(issuedAt, expiresAt, now3) {
|
|
|
86064
86070
|
throw new SkillSelectionError("SESSION_PLAN_EXPIRED", "The reviewed session reconciliation plan is not currently valid; inspect the receipt and prepare a new plan.");
|
|
86065
86071
|
}
|
|
86066
86072
|
}
|
|
86073
|
+
function selectionMetadata(selection) {
|
|
86074
|
+
return { slug: selection.slug, version: selection.version, bundleDigest: selection.bundleDigest };
|
|
86075
|
+
}
|
|
86067
86076
|
function inspectSkillSession(sessionId, options = {}) {
|
|
86068
86077
|
const snapshot2 = readSkillSessionSnapshot(sessionId, options), { receipt } = snapshot2;
|
|
86078
|
+
const loaded = new Set(receipt.loaded);
|
|
86069
86079
|
return {
|
|
86070
86080
|
sessionId,
|
|
86071
86081
|
path: snapshot2.path,
|
|
@@ -86077,7 +86087,8 @@ function inspectSkillSession(sessionId, options = {}) {
|
|
|
86077
86087
|
profileId: receipt.profile.profileId,
|
|
86078
86088
|
profileRevision: receipt.profile.profileRevision,
|
|
86079
86089
|
selectionCount: receipt.profile.selections.length,
|
|
86080
|
-
loadedCount: receipt.loaded.length
|
|
86090
|
+
loadedCount: receipt.loaded.length,
|
|
86091
|
+
selections: receipt.profile.selections.map((selection) => ({ ...selectionMetadata(selection), loaded: loaded.has(selectionKey(selection)) }))
|
|
86081
86092
|
};
|
|
86082
86093
|
}
|
|
86083
86094
|
function renewalDeadlineSignal(error61) {
|
|
@@ -86299,6 +86310,21 @@ async function reconcileSkillSession(input2, options = {}) {
|
|
|
86299
86310
|
}
|
|
86300
86311
|
const targetKeys = new Set(target.selections.map(selectionKey));
|
|
86301
86312
|
const loaded = old.loaded.filter((key) => targetKeys.has(key));
|
|
86313
|
+
const previouslyLoaded = new Set(old.loaded);
|
|
86314
|
+
const targetBySlug = new Map(target.selections.map((selection) => [selection.slug, selection]));
|
|
86315
|
+
const previousSlugs = new Set(old.profile.selections.map((selection) => selection.slug));
|
|
86316
|
+
const selectionDelta = old.profile.selections.map((previous) => {
|
|
86317
|
+
const current = targetBySlug.get(previous.slug);
|
|
86318
|
+
const wasLoaded = previouslyLoaded.has(selectionKey(previous));
|
|
86319
|
+
const reason2 = !current ? "selection-removed" : selectionKey(previous) === selectionKey(current) ? "same-bundle" : "bundle-changed";
|
|
86320
|
+
return {
|
|
86321
|
+
selection: selectionMetadata(previous),
|
|
86322
|
+
loaded: wasLoaded,
|
|
86323
|
+
outcome: !wasLoaded ? "unloaded" : reason2 === "same-bundle" ? "retained" : "retired",
|
|
86324
|
+
reason: reason2,
|
|
86325
|
+
...reason2 === "bundle-changed" ? { replacement: selectionMetadata(current) } : {}
|
|
86326
|
+
};
|
|
86327
|
+
});
|
|
86302
86328
|
const replacementGeneration = nextSkillSessionGeneration(snapshot2.generation);
|
|
86303
86329
|
const plan = {
|
|
86304
86330
|
schemaVersion: 1,
|
|
@@ -86311,6 +86337,8 @@ async function reconcileSkillSession(input2, options = {}) {
|
|
|
86311
86337
|
replacementGeneration,
|
|
86312
86338
|
retainedLoadedCount: loaded.length,
|
|
86313
86339
|
retiredLoadedCount: old.loaded.length - loaded.length,
|
|
86340
|
+
selectionDelta,
|
|
86341
|
+
addedSelections: target.selections.filter((selection) => !previousSlugs.has(selection.slug)).map(selectionMetadata),
|
|
86314
86342
|
scope: "Only this receipt; existing child sessions, project locks, shared profiles, hooks and running processes are unchanged."
|
|
86315
86343
|
};
|
|
86316
86344
|
const planDigest = digest3(plan);
|
|
@@ -86350,6 +86378,9 @@ __export(exports_context, {
|
|
|
86350
86378
|
contextResolverOptions: () => contextResolverOptions
|
|
86351
86379
|
});
|
|
86352
86380
|
import { readSync as readSync12 } from "fs";
|
|
86381
|
+
function sessionSelectionLabel(selection) {
|
|
86382
|
+
return `${selection.slug}@${selection.version} ${selection.bundleDigest}`;
|
|
86383
|
+
}
|
|
86353
86384
|
function selectedProfileId(explicit) {
|
|
86354
86385
|
return explicit ?? process.env.HASNA_SKILLS_SELECTION_PROFILE ?? readManagedSkillPolicy()?.profileId ?? "default";
|
|
86355
86386
|
}
|
|
@@ -86367,8 +86398,12 @@ function registerContextCommands(parent) {
|
|
|
86367
86398
|
sessions.command("show <id>").option("--json", "Return metadata as JSON", false).description("Inspect one session receipt without displaying payloads or changing its pin").action(async (id, options) => {
|
|
86368
86399
|
try {
|
|
86369
86400
|
const result2 = inspectSkillSession(id);
|
|
86370
|
-
await writeCliOutput(options.json ? JSON.stringify(result2) :
|
|
86371
|
-
|
|
86401
|
+
await writeCliOutput(options.json ? JSON.stringify(result2) : [
|
|
86402
|
+
`${JSON.stringify(id)}: ${result2.profileId} at ${result2.profileRevision}`,
|
|
86403
|
+
`Receipt SHA256: ${result2.receiptSha256}`,
|
|
86404
|
+
...result2.selections.map((selection) => `${selection.loaded ? "Loaded" : "Unloaded"}: ${sessionSelectionLabel(selection)}`)
|
|
86405
|
+
].join(`
|
|
86406
|
+
`));
|
|
86372
86407
|
} catch (error61) {
|
|
86373
86408
|
reportContextError(error61, options.json);
|
|
86374
86409
|
}
|
|
@@ -86395,7 +86430,13 @@ Review digest: ${result2.reviewDigest}`);
|
|
|
86395
86430
|
try {
|
|
86396
86431
|
const result2 = await reconcileSkillSession({ ...options, sessionId: id });
|
|
86397
86432
|
await writeCliOutput(options.json ? JSON.stringify(result2) : result2.applied ? `Reconciled ${JSON.stringify(id)} to ${result2.plan.target.profileId} at ${result2.plan.target.profileRevision}.
|
|
86398
|
-
Preserved original receipt: ${result2.archivePath}` :
|
|
86433
|
+
Preserved original receipt: ${result2.archivePath}` : [
|
|
86434
|
+
`Planned one session reconciliation: ${result2.plan.retainedLoadedCount} loaded retained, ${result2.plan.retiredLoadedCount} loaded retired.`,
|
|
86435
|
+
...result2.plan.selectionDelta.map((row) => `${row.outcome} (${row.reason}): ${sessionSelectionLabel(row.selection)}${row.replacement ? ` -> ${sessionSelectionLabel(row.replacement)}` : ""}`),
|
|
86436
|
+
...result2.plan.addedSelections.map((selection) => `Added selection (unloaded): ${sessionSelectionLabel(selection)}`),
|
|
86437
|
+
`Review --json output, then use --apply --plan-digest ${result2.planDigest} --plan-issued-at ${result2.plan.issuedAt} --plan-expires-at ${result2.plan.expiresAt}.`
|
|
86438
|
+
].join(`
|
|
86439
|
+
`));
|
|
86399
86440
|
} catch (error61) {
|
|
86400
86441
|
reportContextError(error61, options.json);
|
|
86401
86442
|
}
|
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.10.
|
|
20022
|
+
version: "0.10.55",
|
|
20023
20023
|
description: "Skills library for AI coding agents",
|
|
20024
20024
|
type: "module",
|
|
20025
20025
|
bin: {
|
|
@@ -38560,7 +38560,8 @@ var init_codex_native_compatibility = __esm(() => {
|
|
|
38560
38560
|
"codex-cli 0.159.0": ["hooks"],
|
|
38561
38561
|
"codex-cli 0.159.2": ["hooks", "qualified-skill-catalog"],
|
|
38562
38562
|
"codex-cli 0.160.0": ["hooks", "qualified-skill-catalog", "installed-plugin-review"],
|
|
38563
|
-
"codex-cli 0.160.1": ["hooks", "qualified-skill-catalog", "installed-plugin-review"]
|
|
38563
|
+
"codex-cli 0.160.1": ["hooks", "qualified-skill-catalog", "installed-plugin-review"],
|
|
38564
|
+
"codex-cli 0.161.0": ["hooks", "qualified-skill-catalog", "installed-plugin-review"]
|
|
38564
38565
|
};
|
|
38565
38566
|
SUPPORTED_CODEX_HOOK_VERSIONS = Object.freeze(Object.keys(MEASURED_CODEX_CAPABILITIES).filter((version2) => supportsCodexNativeCapability(version2, "hooks")));
|
|
38566
38567
|
});
|
|
@@ -50916,12 +50917,17 @@ function retiredCodexRoots(binding) {
|
|
|
50916
50917
|
}
|
|
50917
50918
|
return proof.roots;
|
|
50918
50919
|
}
|
|
50920
|
+
function retainableAbsentSource(source) {
|
|
50921
|
+
if (source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined)
|
|
50922
|
+
return false;
|
|
50923
|
+
return [undefined, "bytes"].includes(source.hashMode) || source.hashMode === "claude-plugin-manifest-v1" && source.sha256 !== null && source.path.endsWith(join26(sep6, ".claude-plugin", "plugin.json"));
|
|
50924
|
+
}
|
|
50919
50925
|
function absentRetiredClaudeRoots(binding) {
|
|
50920
50926
|
if (binding.agent !== "claude" || binding.method !== "reviewed")
|
|
50921
50927
|
return [];
|
|
50922
50928
|
const result = new Set;
|
|
50923
50929
|
for (const source of binding.sources) {
|
|
50924
|
-
if (
|
|
50930
|
+
if (!retainableAbsentSource(source) || source.sha256 === null)
|
|
50925
50931
|
continue;
|
|
50926
50932
|
const marker = sep6 + ".claude" + sep6 + "plugins" + sep6 + "cache" + sep6, at = source.path.indexOf(marker);
|
|
50927
50933
|
if (at < 0 || source.path.indexOf(marker, at + 1) >= 0)
|
|
@@ -51033,7 +51039,7 @@ function verifyAgentDiscovery(binding, codexRecovery) {
|
|
|
51033
51039
|
safe4(source.path);
|
|
51034
51040
|
continue;
|
|
51035
51041
|
}
|
|
51036
|
-
if (absentRoots.some((root) => source.path.startsWith(root + sep6)) &&
|
|
51042
|
+
if (absentRoots.some((root) => source.path.startsWith(root + sep6)) && retainableAbsentSource(source)) {
|
|
51037
51043
|
safe4(source.path);
|
|
51038
51044
|
continue;
|
|
51039
51045
|
}
|
package/bin/migrate.js
CHANGED
package/bin/server.js
CHANGED
|
@@ -24140,7 +24140,7 @@ restoreLaunchCwd();
|
|
|
24140
24140
|
// package.json
|
|
24141
24141
|
var package_default = {
|
|
24142
24142
|
name: "@hasna/skills",
|
|
24143
|
-
version: "0.10.
|
|
24143
|
+
version: "0.10.55",
|
|
24144
24144
|
description: "Skills library for AI coding agents",
|
|
24145
24145
|
type: "module",
|
|
24146
24146
|
bin: {
|
package/bin/worker.js
CHANGED
|
@@ -24143,7 +24143,7 @@ import { randomUUID as randomUUID6 } from "crypto";
|
|
|
24143
24143
|
// package.json
|
|
24144
24144
|
var package_default = {
|
|
24145
24145
|
name: "@hasna/skills",
|
|
24146
|
-
version: "0.10.
|
|
24146
|
+
version: "0.10.55",
|
|
24147
24147
|
description: "Skills library for AI coding agents",
|
|
24148
24148
|
type: "module",
|
|
24149
24149
|
bin: {
|
package/dist/index.js
CHANGED
|
@@ -9398,7 +9398,8 @@ var MEASURED_CODEX_CAPABILITIES = {
|
|
|
9398
9398
|
"codex-cli 0.159.0": ["hooks"],
|
|
9399
9399
|
"codex-cli 0.159.2": ["hooks", "qualified-skill-catalog"],
|
|
9400
9400
|
"codex-cli 0.160.0": ["hooks", "qualified-skill-catalog", "installed-plugin-review"],
|
|
9401
|
-
"codex-cli 0.160.1": ["hooks", "qualified-skill-catalog", "installed-plugin-review"]
|
|
9401
|
+
"codex-cli 0.160.1": ["hooks", "qualified-skill-catalog", "installed-plugin-review"],
|
|
9402
|
+
"codex-cli 0.161.0": ["hooks", "qualified-skill-catalog", "installed-plugin-review"]
|
|
9402
9403
|
};
|
|
9403
9404
|
function supportsCodexNativeCapability(version, capability) {
|
|
9404
9405
|
return typeof version === "string" && Object.hasOwn(MEASURED_CODEX_CAPABILITIES, version) && MEASURED_CODEX_CAPABILITIES[version].includes(capability);
|
|
@@ -11832,7 +11833,7 @@ function replaceSkillSession(expected, replacement, plan, options = {}, beforeCo
|
|
|
11832
11833
|
// package.json
|
|
11833
11834
|
var package_default = {
|
|
11834
11835
|
name: "@hasna/skills",
|
|
11835
|
-
version: "0.10.
|
|
11836
|
+
version: "0.10.55",
|
|
11836
11837
|
description: "Skills library for AI coding agents",
|
|
11837
11838
|
type: "module",
|
|
11838
11839
|
bin: {
|
|
@@ -20837,12 +20838,17 @@ function captureRetiredCodexDiscovery(binding, controls, rules) {
|
|
|
20837
20838
|
return { ...current, codexRetiredMaterializations: previous };
|
|
20838
20839
|
return { ...current, codexRetiredMaterializations: { roots, parents: parents3, directories: captureDiscoveryDirectoryProjection(directories.map((item) => item.path), roots) } };
|
|
20839
20840
|
}
|
|
20841
|
+
function retainableAbsentSource(source) {
|
|
20842
|
+
if (source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined)
|
|
20843
|
+
return false;
|
|
20844
|
+
return [undefined, "bytes"].includes(source.hashMode) || source.hashMode === "claude-plugin-manifest-v1" && source.sha256 !== null && source.path.endsWith(join32(sep8, ".claude-plugin", "plugin.json"));
|
|
20845
|
+
}
|
|
20840
20846
|
function absentRetiredClaudeRoots(binding) {
|
|
20841
20847
|
if (binding.agent !== "claude" || binding.method !== "reviewed")
|
|
20842
20848
|
return [];
|
|
20843
20849
|
const result = new Set;
|
|
20844
20850
|
for (const source of binding.sources) {
|
|
20845
|
-
if (
|
|
20851
|
+
if (!retainableAbsentSource(source) || source.sha256 === null)
|
|
20846
20852
|
continue;
|
|
20847
20853
|
const marker = sep8 + ".claude" + sep8 + "plugins" + sep8 + "cache" + sep8, at = source.path.indexOf(marker);
|
|
20848
20854
|
if (at < 0 || source.path.indexOf(marker, at + 1) >= 0)
|
|
@@ -20954,7 +20960,7 @@ function verifyAgentDiscovery(binding, codexRecovery) {
|
|
|
20954
20960
|
safe4(source.path);
|
|
20955
20961
|
continue;
|
|
20956
20962
|
}
|
|
20957
|
-
if (absentRoots.some((root2) => source.path.startsWith(root2 + sep8)) &&
|
|
20963
|
+
if (absentRoots.some((root2) => source.path.startsWith(root2 + sep8)) && retainableAbsentSource(source)) {
|
|
20958
20964
|
safe4(source.path);
|
|
20959
20965
|
continue;
|
|
20960
20966
|
}
|
|
@@ -21010,7 +21016,7 @@ function rebindAgentDiscovery(binding, changes) {
|
|
|
21010
21016
|
const installationRoots = codexInstallationRoots(binding), absentRoots = [...absentCodexSkillRoots(binding), ...absentRetiredClaudeRoots(binding), ...retiredCodexRoots(binding).filter((root2) => !lstatSync25(root2, { throwIfNoEntry: false }))];
|
|
21011
21017
|
const rebound = { ...binding, sources: binding.sources.map((source) => {
|
|
21012
21018
|
assertMarketplaceBinding(binding, source);
|
|
21013
|
-
if (!changes.has(source.path) && (installationRoots.some((root2) => source.path.startsWith(root2 + sep8)) || absentRoots.some((root2) => source.path.startsWith(root2 + sep8)) &&
|
|
21019
|
+
if (!changes.has(source.path) && (installationRoots.some((root2) => source.path.startsWith(root2 + sep8)) || absentRoots.some((root2) => source.path.startsWith(root2 + sep8)) && retainableAbsentSource(source)))
|
|
21014
21020
|
return source;
|
|
21015
21021
|
const sha2563 = projected(source, changes, budget2);
|
|
21016
21022
|
if (!changes.has(source.path) && sha2563 !== source.sha256)
|
|
@@ -48144,8 +48150,12 @@ function assertPlanWindow(issuedAt, expiresAt, now) {
|
|
|
48144
48150
|
throw new SkillSelectionError("SESSION_PLAN_EXPIRED", "The reviewed session reconciliation plan is not currently valid; inspect the receipt and prepare a new plan.");
|
|
48145
48151
|
}
|
|
48146
48152
|
}
|
|
48153
|
+
function selectionMetadata(selection) {
|
|
48154
|
+
return { slug: selection.slug, version: selection.version, bundleDigest: selection.bundleDigest };
|
|
48155
|
+
}
|
|
48147
48156
|
function inspectSkillSession(sessionId, options = {}) {
|
|
48148
48157
|
const snapshot2 = readSkillSessionSnapshot(sessionId, options), { receipt } = snapshot2;
|
|
48158
|
+
const loaded = new Set(receipt.loaded);
|
|
48149
48159
|
return {
|
|
48150
48160
|
sessionId,
|
|
48151
48161
|
path: snapshot2.path,
|
|
@@ -48157,7 +48167,8 @@ function inspectSkillSession(sessionId, options = {}) {
|
|
|
48157
48167
|
profileId: receipt.profile.profileId,
|
|
48158
48168
|
profileRevision: receipt.profile.profileRevision,
|
|
48159
48169
|
selectionCount: receipt.profile.selections.length,
|
|
48160
|
-
loadedCount: receipt.loaded.length
|
|
48170
|
+
loadedCount: receipt.loaded.length,
|
|
48171
|
+
selections: receipt.profile.selections.map((selection) => ({ ...selectionMetadata(selection), loaded: loaded.has(selectionKey(selection)) }))
|
|
48161
48172
|
};
|
|
48162
48173
|
}
|
|
48163
48174
|
var SESSION_RENEWAL_REFUSAL_TTL_MS = 5 * 60 * 1000;
|
|
@@ -48192,6 +48203,21 @@ async function reconcileSkillSession(input2, options = {}) {
|
|
|
48192
48203
|
}
|
|
48193
48204
|
const targetKeys = new Set(target.selections.map(selectionKey));
|
|
48194
48205
|
const loaded = old.loaded.filter((key) => targetKeys.has(key));
|
|
48206
|
+
const previouslyLoaded = new Set(old.loaded);
|
|
48207
|
+
const targetBySlug = new Map(target.selections.map((selection) => [selection.slug, selection]));
|
|
48208
|
+
const previousSlugs = new Set(old.profile.selections.map((selection) => selection.slug));
|
|
48209
|
+
const selectionDelta = old.profile.selections.map((previous) => {
|
|
48210
|
+
const current = targetBySlug.get(previous.slug);
|
|
48211
|
+
const wasLoaded = previouslyLoaded.has(selectionKey(previous));
|
|
48212
|
+
const reason2 = !current ? "selection-removed" : selectionKey(previous) === selectionKey(current) ? "same-bundle" : "bundle-changed";
|
|
48213
|
+
return {
|
|
48214
|
+
selection: selectionMetadata(previous),
|
|
48215
|
+
loaded: wasLoaded,
|
|
48216
|
+
outcome: !wasLoaded ? "unloaded" : reason2 === "same-bundle" ? "retained" : "retired",
|
|
48217
|
+
reason: reason2,
|
|
48218
|
+
...reason2 === "bundle-changed" ? { replacement: selectionMetadata(current) } : {}
|
|
48219
|
+
};
|
|
48220
|
+
});
|
|
48195
48221
|
const replacementGeneration = nextSkillSessionGeneration(snapshot2.generation);
|
|
48196
48222
|
const plan = {
|
|
48197
48223
|
schemaVersion: 1,
|
|
@@ -48204,6 +48230,8 @@ async function reconcileSkillSession(input2, options = {}) {
|
|
|
48204
48230
|
replacementGeneration,
|
|
48205
48231
|
retainedLoadedCount: loaded.length,
|
|
48206
48232
|
retiredLoadedCount: old.loaded.length - loaded.length,
|
|
48233
|
+
selectionDelta,
|
|
48234
|
+
addedSelections: target.selections.filter((selection) => !previousSlugs.has(selection.slug)).map(selectionMetadata),
|
|
48207
48235
|
scope: "Only this receipt; existing child sessions, project locks, shared profiles, hooks and running processes are unchanged."
|
|
48208
48236
|
};
|
|
48209
48237
|
const planDigest = digest3(plan);
|
|
@@ -16,6 +16,19 @@ export interface SessionReconciliationOptions extends SelectionCacheOptions {
|
|
|
16
16
|
client?: ProfileClient;
|
|
17
17
|
}
|
|
18
18
|
export declare const SESSION_RECONCILIATION_PLAN_TTL_MS: number;
|
|
19
|
+
/** Exact bundle identity only; never spread receipt, policy or payload fields. */
|
|
20
|
+
export interface SessionSelectionMetadata {
|
|
21
|
+
slug: string;
|
|
22
|
+
version: string;
|
|
23
|
+
bundleDigest: string;
|
|
24
|
+
}
|
|
25
|
+
export interface SessionSelectionDelta {
|
|
26
|
+
selection: SessionSelectionMetadata;
|
|
27
|
+
loaded: boolean;
|
|
28
|
+
outcome: "retained" | "retired" | "unloaded";
|
|
29
|
+
reason: "same-bundle" | "selection-removed" | "bundle-changed";
|
|
30
|
+
replacement?: SessionSelectionMetadata;
|
|
31
|
+
}
|
|
19
32
|
/** Metadata only: inspection never displays skill payloads or resolves credentials. */
|
|
20
33
|
export declare function inspectSkillSession(sessionId: string, options?: SelectionCacheOptions): {
|
|
21
34
|
sessionId: string;
|
|
@@ -29,6 +42,12 @@ export declare function inspectSkillSession(sessionId: string, options?: Selecti
|
|
|
29
42
|
profileRevision: string;
|
|
30
43
|
selectionCount: number;
|
|
31
44
|
loadedCount: number;
|
|
45
|
+
selections: {
|
|
46
|
+
loaded: boolean;
|
|
47
|
+
slug: string;
|
|
48
|
+
version: string;
|
|
49
|
+
bundleDigest: string;
|
|
50
|
+
}[];
|
|
32
51
|
};
|
|
33
52
|
/** Fits inside the managed context child's 6.5-second ceiling, including CLI work. */
|
|
34
53
|
export declare const SESSION_RENEWAL_TIMEOUT_MS = 4000;
|
|
@@ -75,6 +94,8 @@ export declare function reconcileSkillSession(input: SessionReconciliationInput,
|
|
|
75
94
|
replacementGeneration: number;
|
|
76
95
|
retainedLoadedCount: number;
|
|
77
96
|
retiredLoadedCount: number;
|
|
97
|
+
selectionDelta: SessionSelectionDelta[];
|
|
98
|
+
addedSelections: SessionSelectionMetadata[];
|
|
78
99
|
scope: string;
|
|
79
100
|
};
|
|
80
101
|
planDigest: string;
|
|
@@ -112,6 +133,8 @@ export declare function reconcileSkillSession(input: SessionReconciliationInput,
|
|
|
112
133
|
replacementGeneration: number;
|
|
113
134
|
retainedLoadedCount: number;
|
|
114
135
|
retiredLoadedCount: number;
|
|
136
|
+
selectionDelta: SessionSelectionDelta[];
|
|
137
|
+
addedSelections: SessionSelectionMetadata[];
|
|
115
138
|
scope: string;
|
|
116
139
|
};
|
|
117
140
|
planDigest: string;
|
package/dist/sdk/index.d.ts
CHANGED
|
@@ -19,7 +19,7 @@ export { CloudExecutionClient, type CloudExecution, type CloudEligibility } from
|
|
|
19
19
|
export { readExecutionGrantPolicy, saveExecutionGrantPolicy, resolveExecutionGrant } from "../lib/execution-grant-client.js";
|
|
20
20
|
export type { ExecutionGrant, ExecutionGrantPolicy, ExecutionGrantRequest, ResolvedExecutionGrant } from "../lib/execution-grants.js";
|
|
21
21
|
export { HttpProfileClient, createProfileClient, type ProfileClient, type AuthenticatedProfilePrincipal, type AuthenticatedProfileRole } from "../lib/profile-client.js";
|
|
22
|
-
export { inspectSkillSession, reconcileSkillSession, type SessionReconciliationInput, type SessionReconciliationOptions } from "../lib/session-reconciliation.js";
|
|
22
|
+
export { inspectSkillSession, reconcileSkillSession, type SessionSelectionMetadata, type SessionSelectionDelta, type SessionReconciliationInput, type SessionReconciliationOptions } from "../lib/session-reconciliation.js";
|
|
23
23
|
export { resolveSelectedRun, executeSelectedLocal, prepareSelectedSecretBindings, type ResolvedSelectedRun, type SelectedLocalRunOptions } from "../lib/selected-run.js";
|
|
24
24
|
export { readSelectedSecretBindings, type SelectedSecretBindings, type SelectedSecretsClient } from "../lib/execution-secrets.js";
|
|
25
25
|
export type { SkillSelection, SkillProfile, ResolvedSkillSelection, ResolvedSkillProfile, StationSkillStateInput, StationSkillState } from "../types/skill-selection.js";
|
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.10.
|
|
32376
|
+
version: "0.10.55",
|
|
32377
32377
|
description: "Skills library for AI coding agents",
|
|
32378
32378
|
type: "module",
|
|
32379
32379
|
bin: {
|
|
@@ -37490,8 +37490,12 @@ function assertPlanWindow(issuedAt, expiresAt, now) {
|
|
|
37490
37490
|
throw new SkillSelectionError("SESSION_PLAN_EXPIRED", "The reviewed session reconciliation plan is not currently valid; inspect the receipt and prepare a new plan.");
|
|
37491
37491
|
}
|
|
37492
37492
|
}
|
|
37493
|
+
function selectionMetadata(selection2) {
|
|
37494
|
+
return { slug: selection2.slug, version: selection2.version, bundleDigest: selection2.bundleDigest };
|
|
37495
|
+
}
|
|
37493
37496
|
function inspectSkillSession(sessionId, options = {}) {
|
|
37494
37497
|
const snapshot = readSkillSessionSnapshot(sessionId, options), { receipt } = snapshot;
|
|
37498
|
+
const loaded = new Set(receipt.loaded);
|
|
37495
37499
|
return {
|
|
37496
37500
|
sessionId,
|
|
37497
37501
|
path: snapshot.path,
|
|
@@ -37503,7 +37507,8 @@ function inspectSkillSession(sessionId, options = {}) {
|
|
|
37503
37507
|
profileId: receipt.profile.profileId,
|
|
37504
37508
|
profileRevision: receipt.profile.profileRevision,
|
|
37505
37509
|
selectionCount: receipt.profile.selections.length,
|
|
37506
|
-
loadedCount: receipt.loaded.length
|
|
37510
|
+
loadedCount: receipt.loaded.length,
|
|
37511
|
+
selections: receipt.profile.selections.map((selection2) => ({ ...selectionMetadata(selection2), loaded: loaded.has(selectionKey(selection2)) }))
|
|
37507
37512
|
};
|
|
37508
37513
|
}
|
|
37509
37514
|
var SESSION_RENEWAL_REFUSAL_TTL_MS = 5 * 60 * 1000;
|
|
@@ -37538,6 +37543,21 @@ async function reconcileSkillSession(input, options = {}) {
|
|
|
37538
37543
|
}
|
|
37539
37544
|
const targetKeys = new Set(target.selections.map(selectionKey));
|
|
37540
37545
|
const loaded = old.loaded.filter((key) => targetKeys.has(key));
|
|
37546
|
+
const previouslyLoaded = new Set(old.loaded);
|
|
37547
|
+
const targetBySlug = new Map(target.selections.map((selection2) => [selection2.slug, selection2]));
|
|
37548
|
+
const previousSlugs = new Set(old.profile.selections.map((selection2) => selection2.slug));
|
|
37549
|
+
const selectionDelta = old.profile.selections.map((previous) => {
|
|
37550
|
+
const current = targetBySlug.get(previous.slug);
|
|
37551
|
+
const wasLoaded = previouslyLoaded.has(selectionKey(previous));
|
|
37552
|
+
const reason = !current ? "selection-removed" : selectionKey(previous) === selectionKey(current) ? "same-bundle" : "bundle-changed";
|
|
37553
|
+
return {
|
|
37554
|
+
selection: selectionMetadata(previous),
|
|
37555
|
+
loaded: wasLoaded,
|
|
37556
|
+
outcome: !wasLoaded ? "unloaded" : reason === "same-bundle" ? "retained" : "retired",
|
|
37557
|
+
reason,
|
|
37558
|
+
...reason === "bundle-changed" ? { replacement: selectionMetadata(current) } : {}
|
|
37559
|
+
};
|
|
37560
|
+
});
|
|
37541
37561
|
const replacementGeneration = nextSkillSessionGeneration(snapshot.generation);
|
|
37542
37562
|
const plan = {
|
|
37543
37563
|
schemaVersion: 1,
|
|
@@ -37550,6 +37570,8 @@ async function reconcileSkillSession(input, options = {}) {
|
|
|
37550
37570
|
replacementGeneration,
|
|
37551
37571
|
retainedLoadedCount: loaded.length,
|
|
37552
37572
|
retiredLoadedCount: old.loaded.length - loaded.length,
|
|
37573
|
+
selectionDelta,
|
|
37574
|
+
addedSelections: target.selections.filter((selection2) => !previousSlugs.has(selection2.slug)).map(selectionMetadata),
|
|
37553
37575
|
scope: "Only this receipt; existing child sessions, project locks, shared profiles, hooks and running processes are unchanged."
|
|
37554
37576
|
};
|
|
37555
37577
|
const planDigest = digest(plan);
|
|
@@ -38446,7 +38468,8 @@ var MEASURED_CODEX_CAPABILITIES = {
|
|
|
38446
38468
|
"codex-cli 0.159.0": ["hooks"],
|
|
38447
38469
|
"codex-cli 0.159.2": ["hooks", "qualified-skill-catalog"],
|
|
38448
38470
|
"codex-cli 0.160.0": ["hooks", "qualified-skill-catalog", "installed-plugin-review"],
|
|
38449
|
-
"codex-cli 0.160.1": ["hooks", "qualified-skill-catalog", "installed-plugin-review"]
|
|
38471
|
+
"codex-cli 0.160.1": ["hooks", "qualified-skill-catalog", "installed-plugin-review"],
|
|
38472
|
+
"codex-cli 0.161.0": ["hooks", "qualified-skill-catalog", "installed-plugin-review"]
|
|
38450
38473
|
};
|
|
38451
38474
|
function supportsCodexNativeCapability(version, capability) {
|
|
38452
38475
|
return typeof version === "string" && Object.hasOwn(MEASURED_CODEX_CAPABILITIES, version) && MEASURED_CODEX_CAPABILITIES[version].includes(capability);
|
|
@@ -43655,12 +43678,17 @@ function captureRetiredCodexDiscovery(binding, controls, rules) {
|
|
|
43655
43678
|
return { ...current, codexRetiredMaterializations: previous };
|
|
43656
43679
|
return { ...current, codexRetiredMaterializations: { roots, parents: parents3, directories: captureDiscoveryDirectoryProjection(directories.map((item) => item.path), roots) } };
|
|
43657
43680
|
}
|
|
43681
|
+
function retainableAbsentSource(source) {
|
|
43682
|
+
if (source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined)
|
|
43683
|
+
return false;
|
|
43684
|
+
return [undefined, "bytes"].includes(source.hashMode) || source.hashMode === "claude-plugin-manifest-v1" && source.sha256 !== null && source.path.endsWith(join26(sep6, ".claude-plugin", "plugin.json"));
|
|
43685
|
+
}
|
|
43658
43686
|
function absentRetiredClaudeRoots(binding) {
|
|
43659
43687
|
if (binding.agent !== "claude" || binding.method !== "reviewed")
|
|
43660
43688
|
return [];
|
|
43661
43689
|
const result = new Set;
|
|
43662
43690
|
for (const source of binding.sources) {
|
|
43663
|
-
if (
|
|
43691
|
+
if (!retainableAbsentSource(source) || source.sha256 === null)
|
|
43664
43692
|
continue;
|
|
43665
43693
|
const marker = sep6 + ".claude" + sep6 + "plugins" + sep6 + "cache" + sep6, at = source.path.indexOf(marker);
|
|
43666
43694
|
if (at < 0 || source.path.indexOf(marker, at + 1) >= 0)
|
|
@@ -43772,7 +43800,7 @@ function verifyAgentDiscovery(binding, codexRecovery) {
|
|
|
43772
43800
|
safe4(source.path);
|
|
43773
43801
|
continue;
|
|
43774
43802
|
}
|
|
43775
|
-
if (absentRoots.some((root2) => source.path.startsWith(root2 + sep6)) &&
|
|
43803
|
+
if (absentRoots.some((root2) => source.path.startsWith(root2 + sep6)) && retainableAbsentSource(source)) {
|
|
43776
43804
|
safe4(source.path);
|
|
43777
43805
|
continue;
|
|
43778
43806
|
}
|
|
@@ -43828,7 +43856,7 @@ function rebindAgentDiscovery(binding, changes) {
|
|
|
43828
43856
|
const installationRoots = codexInstallationRoots(binding), absentRoots = [...absentCodexSkillRoots(binding), ...absentRetiredClaudeRoots(binding), ...retiredCodexRoots(binding).filter((root2) => !lstatSync21(root2, { throwIfNoEntry: false }))];
|
|
43829
43857
|
const rebound = { ...binding, sources: binding.sources.map((source) => {
|
|
43830
43858
|
assertMarketplaceBinding(binding, source);
|
|
43831
|
-
if (!changes.has(source.path) && (installationRoots.some((root2) => source.path.startsWith(root2 + sep6)) || absentRoots.some((root2) => source.path.startsWith(root2 + sep6)) &&
|
|
43859
|
+
if (!changes.has(source.path) && (installationRoots.some((root2) => source.path.startsWith(root2 + sep6)) || absentRoots.some((root2) => source.path.startsWith(root2 + sep6)) && retainableAbsentSource(source)))
|
|
43832
43860
|
return source;
|
|
43833
43861
|
const sha2562 = projected(source, changes, budget2);
|
|
43834
43862
|
if (!changes.has(source.path) && sha2562 !== source.sha256)
|