@hasna/skills 0.10.73 → 0.10.74
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 +77 -0
- package/bin/index.js +516 -45
- package/bin/mcp.js +79 -12
- package/bin/migrate.js +1 -1
- package/bin/server.js +1 -1
- package/bin/worker.js +1 -1
- package/dist/cli/commands/provenance.d.ts +18 -0
- package/dist/index.js +74 -22
- package/dist/lib/portable-skills-files.d.ts +10 -0
- package/dist/lib/selected-run.d.ts +45 -0
- package/dist/lib/selection-provenance.d.ts +108 -0
- package/dist/runtime-prerequisites.js +1 -1
- package/dist/sdk/index.js +123 -27
- package/docs/plugin-admission.md +13 -9
- package/docs/skill-standard.md +18 -4
- package/package.json +1 -1
package/bin/mcp.js
CHANGED
|
@@ -23,7 +23,7 @@ var package_default;
|
|
|
23
23
|
var init_package = __esm(() => {
|
|
24
24
|
package_default = {
|
|
25
25
|
name: "@hasna/skills",
|
|
26
|
-
version: "0.10.
|
|
26
|
+
version: "0.10.74",
|
|
27
27
|
description: "Skills library for AI coding agents",
|
|
28
28
|
type: "module",
|
|
29
29
|
bin: {
|
|
@@ -22021,8 +22021,8 @@ function ensureInstructionSkillFiles(skillPath, manifest) {
|
|
|
22021
22021
|
displayName: manifest.displayName ?? displayName(manifest.name),
|
|
22022
22022
|
category: manifest.category ?? "Development Tools",
|
|
22023
22023
|
tags: manifest.tags?.length ? manifest.tags : ["custom", manifest.name],
|
|
22024
|
-
inputs: [],
|
|
22025
|
-
commands: []
|
|
22024
|
+
inputs: Array.isArray(manifest.inputs) ? manifest.inputs : [],
|
|
22025
|
+
commands: Array.isArray(manifest.commands) ? manifest.commands : []
|
|
22026
22026
|
};
|
|
22027
22027
|
if (!existsSync6(join9(skillPath, "SKILL.md"))) {
|
|
22028
22028
|
writeFileSync2(join9(skillPath, "SKILL.md"), renderSkillMd(next));
|
|
@@ -37726,6 +37726,12 @@ function verifyAgentDiscovery(binding, codexRecovery, reviewedRootAliases, witne
|
|
|
37726
37726
|
throw new Error("Retired native materialization changed during discovery verification");
|
|
37727
37727
|
}
|
|
37728
37728
|
}
|
|
37729
|
+
function claudeControlSource(source) {
|
|
37730
|
+
return CLAUDE_CONTROL_WITNESS_MODES.has(source.hashMode ?? "") || CLAUDE_CONTROL_FILES.has(basename12(source.path)) && !source.path.includes(`${sep11}.claude${sep11}plugins${sep11}cache${sep11}`);
|
|
37731
|
+
}
|
|
37732
|
+
function retainedAbsentClaudeRoot(absentRoots, source) {
|
|
37733
|
+
return claudeControlSource(source) ? undefined : retainedAbsentRoot(absentRoots, source);
|
|
37734
|
+
}
|
|
37729
37735
|
function codexInstallationRoots(binding) {
|
|
37730
37736
|
const proof = binding.codexInstallationInputs;
|
|
37731
37737
|
if (proof === undefined)
|
|
@@ -37921,7 +37927,10 @@ function resolveAgentDiscovery(options) {
|
|
|
37921
37927
|
if (!review.sources.some((source) => source.path === canonical9(configPath)))
|
|
37922
37928
|
throw new Error("Discovery review must include the agent configuration source");
|
|
37923
37929
|
const reviewedSources = supersedesSettingsProjection(agent, review.sources, canonical9(configPath)) ? sources.filter((source) => !isSupersededSettingsProjection(agent, canonical9(configPath), source)) : sources;
|
|
37930
|
+
const absentRoots = absentRetiredClaudeRoots(supplied);
|
|
37924
37931
|
const reviewedSourcesWithManifestProjection = review.sources.map((source) => {
|
|
37932
|
+
if (retainedAbsentClaudeRoot(absentRoots, source) !== undefined)
|
|
37933
|
+
return source;
|
|
37925
37934
|
if (agent === "claude" && source.format === undefined && source.fields === undefined && source.managedPlugins === undefined && source.reviewArtifact === undefined && [undefined, "bytes", "path-bytes"].includes(source.hashMode) && isClaudePluginCacheOrphanMarker(source.path)) {
|
|
37926
37935
|
return { path: source.path, hashMode: CLAUDE_ORPHAN_MARKER_MODE, sha256: hashClaudePluginCacheOrphanMarker(source.path) };
|
|
37927
37936
|
}
|
|
@@ -37954,6 +37963,11 @@ function resolveAgentDiscovery(options) {
|
|
|
37954
37963
|
});
|
|
37955
37964
|
if (agent === "claude")
|
|
37956
37965
|
assertReviewedClaudeCacheClosures(home, config2, review, canonical9);
|
|
37966
|
+
for (const root of absentRoots) {
|
|
37967
|
+
safe6(root);
|
|
37968
|
+
if (lstatSync23(root, { throwIfNoEntry: false }))
|
|
37969
|
+
throw new Error("Retired native materialization changed during discovery review");
|
|
37970
|
+
}
|
|
37957
37971
|
const combined = [...reviewedSources, ...reviewedSourcesWithManifestProjection];
|
|
37958
37972
|
const unique = combined.filter((source, index) => combined.findIndex((candidate) => isDeepStrictEqual5(candidate, source)) === index);
|
|
37959
37973
|
return { ...supplied, roots: [...new Set([...roots, ...supplied.roots])].sort(), sources: unique, ...agent === "gemini" ? { builtinNames } : {} };
|
|
@@ -46399,7 +46413,11 @@ var exports_selected_run = {};
|
|
|
46399
46413
|
__export(exports_selected_run, {
|
|
46400
46414
|
resolveSelectedRun: () => resolveSelectedRun,
|
|
46401
46415
|
prepareSelectedSecretBindings: () => prepareSelectedSecretBindings,
|
|
46402
|
-
|
|
46416
|
+
instructionRunRefusal: () => instructionRunRefusal,
|
|
46417
|
+
instructionEntrypointMismatch: () => instructionEntrypointMismatch,
|
|
46418
|
+
instructionCommandEntry: () => instructionCommandEntry,
|
|
46419
|
+
executeSelectedLocal: () => executeSelectedLocal,
|
|
46420
|
+
declaredOperation: () => declaredOperation
|
|
46403
46421
|
});
|
|
46404
46422
|
import { createHash as createHash28 } from "crypto";
|
|
46405
46423
|
import { spawn as spawn2 } from "child_process";
|
|
@@ -46412,12 +46430,51 @@ async function resolveSelectedRun(spec, profileId, options = {}) {
|
|
|
46412
46430
|
const entries = await readSelectedEntries(selection2, context2, options);
|
|
46413
46431
|
return { selection: selection2, profileId: context2.receipt.profile.profileId, entries, ...describeEntries(entries), cacheDir: selectionCacheRoot(options) };
|
|
46414
46432
|
}
|
|
46433
|
+
function declaredOperation(manifest) {
|
|
46434
|
+
const entry = instructionCommandEntry(manifest);
|
|
46435
|
+
if (!entry)
|
|
46436
|
+
return null;
|
|
46437
|
+
const runtime = manifest.runtime;
|
|
46438
|
+
if (!runtime || typeof runtime !== "object" || Array.isArray(runtime))
|
|
46439
|
+
return null;
|
|
46440
|
+
if (runtime.runtime !== "bun" && runtime.runtime !== "node" && runtime.runtime !== "python3")
|
|
46441
|
+
return null;
|
|
46442
|
+
if (instructionEntrypointMismatch(manifest))
|
|
46443
|
+
return null;
|
|
46444
|
+
return { entry };
|
|
46445
|
+
}
|
|
46446
|
+
function instructionCommandEntry(manifest) {
|
|
46447
|
+
const commands = Array.isArray(manifest.commands) ? manifest.commands : [];
|
|
46448
|
+
const command = commands[0];
|
|
46449
|
+
if (!command || typeof command !== "object" || typeof command.entry !== "string" || command.entry.length === 0)
|
|
46450
|
+
return null;
|
|
46451
|
+
return command.entry;
|
|
46452
|
+
}
|
|
46453
|
+
function instructionEntrypointMismatch(manifest) {
|
|
46454
|
+
const entry = instructionCommandEntry(manifest);
|
|
46455
|
+
const declared = manifest.runtime;
|
|
46456
|
+
if (!entry || !declared || typeof declared !== "object" || Array.isArray(declared))
|
|
46457
|
+
return null;
|
|
46458
|
+
const runtimeEntrypoint = declared.entrypoint;
|
|
46459
|
+
return typeof runtimeEntrypoint === "string" && runtimeEntrypoint !== entry ? runtimeEntrypoint : null;
|
|
46460
|
+
}
|
|
46461
|
+
function instructionRunRefusal(manifest, isBundledFile) {
|
|
46462
|
+
const declaredEntry = instructionCommandEntry(manifest);
|
|
46463
|
+
if (declaredEntry && !isBundledFile(declaredEntry))
|
|
46464
|
+
return { code: "LOCAL_ENTRYPOINT_MISSING", message: `This selected instruction skill declares its operation entry as ${declaredEntry}, which is not a regular file in the selected bundle; a different runtime.entrypoint never substitutes for it.` };
|
|
46465
|
+
const mismatch = instructionEntrypointMismatch(manifest);
|
|
46466
|
+
if (mismatch)
|
|
46467
|
+
return { code: "LOCAL_ENTRYPOINT_MISMATCH", message: `This selected instruction skill declares its operation entry as ${declaredEntry}, but its runtime.entrypoint is ${mismatch}; the two must agree.` };
|
|
46468
|
+
if (!declaredOperation(manifest))
|
|
46469
|
+
return { code: "INSTRUCTION_SKILL", message: "This selected skill contains instructions. Use skills load instead of skills run." };
|
|
46470
|
+
return null;
|
|
46471
|
+
}
|
|
46415
46472
|
async function prepareSelectedSecretBindings(selected, cwd = process.cwd()) {
|
|
46416
46473
|
const entries = await readCachedSelection(selected.selection, { cacheDir: selected.cacheDir });
|
|
46417
46474
|
if (!entries)
|
|
46418
46475
|
throw new SkillSelectionError("CACHED_BUNDLE_MISSING", "The selected execution bundle is not cached.");
|
|
46419
46476
|
const { kind, manifest } = describeEntries(entries);
|
|
46420
|
-
if (kind !== "executable")
|
|
46477
|
+
if (kind !== "executable" && !declaredOperation(manifest))
|
|
46421
46478
|
throw new SkillSelectionError("INSTRUCTION_SKILL", "Instruction skills do not accept execution secret bindings.");
|
|
46422
46479
|
const names = declaredSecretNames(manifest.runtime?.env);
|
|
46423
46480
|
if (!names.length)
|
|
@@ -46430,8 +46487,12 @@ async function executeSelectedLocal(selected, options = {}) {
|
|
|
46430
46487
|
if (!entries)
|
|
46431
46488
|
throw new SkillSelectionError("CACHED_BUNDLE_MISSING", "The selected local execution bundle is not cached.");
|
|
46432
46489
|
const { kind, manifest, packageJson } = describeEntries(entries);
|
|
46433
|
-
|
|
46434
|
-
|
|
46490
|
+
const operation = declaredOperation(manifest);
|
|
46491
|
+
if (kind === "instruction") {
|
|
46492
|
+
const refusal3 = instructionRunRefusal(manifest, (path) => entries.some((entry) => entry.path === path));
|
|
46493
|
+
if (refusal3)
|
|
46494
|
+
throw new SkillSelectionError(refusal3.code, refusal3.message);
|
|
46495
|
+
}
|
|
46435
46496
|
const runtime = manifest.runtime ?? {};
|
|
46436
46497
|
if (!runtime || typeof runtime !== "object" || Array.isArray(runtime))
|
|
46437
46498
|
throw new SkillSelectionError("INVALID_SKILL_MANIFEST", "The selected runtime declaration is invalid.");
|
|
@@ -46450,9 +46511,9 @@ async function executeSelectedLocal(selected, options = {}) {
|
|
|
46450
46511
|
const executable = runtimeName === "bun" ? process.execPath : Bun.which(runtimeName);
|
|
46451
46512
|
if (!executable)
|
|
46452
46513
|
throw new SkillSelectionError("LOCAL_RUNTIME_UNAVAILABLE", "The selected runtime is not installed on this station.");
|
|
46453
|
-
const declaredEntry = runtime.entrypoint ?? (typeof packageJson.bin === "string" ? packageJson.bin : packageJson.bin && typeof packageJson.bin === "object" ? Object.values(packageJson.bin)[0] : undefined);
|
|
46514
|
+
const declaredEntry = (kind === "instruction" ? operation?.entry : undefined) ?? runtime.entrypoint ?? (typeof packageJson.bin === "string" ? packageJson.bin : packageJson.bin && typeof packageJson.bin === "object" ? Object.values(packageJson.bin)[0] : undefined);
|
|
46454
46515
|
if (typeof declaredEntry !== "string" || !entries.some((entry) => entry.path === declaredEntry))
|
|
46455
|
-
throw new SkillSelectionError("LOCAL_ENTRYPOINT_MISSING", "The selected bundle must declare a regular-file entrypoint in its runtime manifest or package bin.");
|
|
46516
|
+
throw new SkillSelectionError("LOCAL_ENTRYPOINT_MISSING", kind === "instruction" ? "The selected instruction skill must declare the operation entrypoint it ships in its manifest, and that declared entry must be a regular file in the selected bundle; a different runtime.entrypoint never substitutes for it." : "The selected bundle must declare a regular-file entrypoint in its runtime manifest or package bin.");
|
|
46456
46517
|
const args = options.args ?? [];
|
|
46457
46518
|
if (args.length > 128 || args.some((arg) => typeof arg !== "string" || arg.length > 16384 || arg.includes("\x00")))
|
|
46458
46519
|
throw new SkillSelectionError("INVALID_RUN_INPUT", "Local skill arguments exceed their limits.");
|
|
@@ -46527,6 +46588,7 @@ async function executeSelectedLocal(selected, options = {}) {
|
|
|
46527
46588
|
selection: selection2,
|
|
46528
46589
|
target: "local",
|
|
46529
46590
|
runtime: runtimeName,
|
|
46591
|
+
entrypoint: declaredEntry,
|
|
46530
46592
|
inputDigest: `sha256:${createHash28("sha256").update(input2).digest("hex")}`,
|
|
46531
46593
|
runDirectory,
|
|
46532
46594
|
...executionGrant ? { executionGrant } : {},
|
|
@@ -46973,13 +47035,18 @@ function registerOperationTools(server) {
|
|
|
46973
47035
|
return mcpError("CONFLICTING_EXECUTION_MODES", "Use remote for the configured server catalog and quote, or target for selected execution, not both.");
|
|
46974
47036
|
if (managed && !remote) {
|
|
46975
47037
|
try {
|
|
46976
|
-
const { resolveSelectedRun: resolveSelectedRun2, executeSelectedLocal: executeSelectedLocal2 } = await Promise.resolve().then(() => (init_selected_run(), exports_selected_run));
|
|
47038
|
+
const { resolveSelectedRun: resolveSelectedRun2, executeSelectedLocal: executeSelectedLocal2, instructionRunRefusal: instructionRunRefusal2 } = await Promise.resolve().then(() => (init_selected_run(), exports_selected_run));
|
|
46977
47039
|
const resolved = await resolveSelectedRun2(name, selectedProfileId(), { projectDir: process.cwd() });
|
|
46978
|
-
if (resolved.kind === "instruction")
|
|
46979
|
-
|
|
47040
|
+
if (resolved.kind === "instruction") {
|
|
47041
|
+
const refusal3 = instructionRunRefusal2(resolved.manifest, (path) => resolved.entries.some((entry) => entry.path === path));
|
|
47042
|
+
if (refusal3)
|
|
47043
|
+
return mcpError(refusal3.code, refusal3.message);
|
|
47044
|
+
}
|
|
46980
47045
|
if (files?.length)
|
|
46981
47046
|
return mcpError("SELECTED_INPUT_REQUIRED", "Selected executions require their declared JSON input; use the CLI cloud execution input contract for files.");
|
|
46982
47047
|
if (target === "cloud") {
|
|
47048
|
+
if (resolved.kind === "instruction")
|
|
47049
|
+
return mcpError("INSTRUCTION_SKILL", "Cloud execution requires an executable skill version; run the declared operation locally on a managed station, or load the instructions with skills load.");
|
|
46983
47050
|
if (args?.length || quoteReceipt || maxCredits !== undefined || maxCostCents !== undefined)
|
|
46984
47051
|
return mcpError("SELECTED_INPUT_REQUIRED", "The selected cloud execution contract accepts declared JSON input, not legacy run arguments or quote receipts.");
|
|
46985
47052
|
const { CloudExecutionClient: CloudExecutionClient2 } = await Promise.resolve().then(() => (init_cloud_executions(), exports_cloud_executions));
|
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.74",
|
|
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.74",
|
|
24147
24147
|
description: "Skills library for AI coding agents",
|
|
24148
24148
|
type: "module",
|
|
24149
24149
|
bin: {
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Command } from "commander";
|
|
2
|
+
import { type SelectionProvenanceReceipt } from "../../lib/selection-provenance.js";
|
|
3
|
+
export interface ProvenanceCommandOptions {
|
|
4
|
+
selectionProfile?: string;
|
|
5
|
+
station?: string;
|
|
6
|
+
session?: string;
|
|
7
|
+
cached?: boolean;
|
|
8
|
+
json?: boolean;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* One read-only receipt for the selection chain: selected, activated, applied,
|
|
12
|
+
* pinned and consumed are named separately, and every consumer carries its
|
|
13
|
+
* status and reason. An exit code here only reports a bad request: a read that
|
|
14
|
+
* could not be answered is recorded as `unverified`, because that is the fact
|
|
15
|
+
* the caller came for.
|
|
16
|
+
*/
|
|
17
|
+
export declare function provenanceLines(receipt: SelectionProvenanceReceipt): string[];
|
|
18
|
+
export declare function registerProvenance(parent: Command): void;
|
package/dist/index.js
CHANGED
|
@@ -8566,8 +8566,8 @@ function ensureInstructionSkillFiles(skillPath, manifest) {
|
|
|
8566
8566
|
displayName: manifest.displayName ?? displayName(manifest.name),
|
|
8567
8567
|
category: manifest.category ?? "Development Tools",
|
|
8568
8568
|
tags: manifest.tags?.length ? manifest.tags : ["custom", manifest.name],
|
|
8569
|
-
inputs: [],
|
|
8570
|
-
commands: []
|
|
8569
|
+
inputs: Array.isArray(manifest.inputs) ? manifest.inputs : [],
|
|
8570
|
+
commands: Array.isArray(manifest.commands) ? manifest.commands : []
|
|
8571
8571
|
};
|
|
8572
8572
|
if (!existsSync6(join6(skillPath, "SKILL.md"))) {
|
|
8573
8573
|
writeFileSync2(join6(skillPath, "SKILL.md"), renderSkillMd(next));
|
|
@@ -15144,7 +15144,7 @@ import { basename as basename13, dirname as dirname24, isAbsolute as isAbsolute2
|
|
|
15144
15144
|
import { homedir as homedir5 } from "os";
|
|
15145
15145
|
import { isAbsolute as isAbsolute25, join as join36, resolve as resolve24 } from "path";
|
|
15146
15146
|
// package.json
|
|
15147
|
-
var version = "0.10.
|
|
15147
|
+
var version = "0.10.74";
|
|
15148
15148
|
var package_default = {
|
|
15149
15149
|
name: "@hasna/skills",
|
|
15150
15150
|
version,
|
|
@@ -20212,11 +20212,19 @@ function planAgentIntegration(options) {
|
|
|
20212
20212
|
const executable2 = discoveryExecutable(agent) ?? (prior ? recordedDiscoveryExecutable(agent, prior) : null);
|
|
20213
20213
|
executables.set(agent, executable2);
|
|
20214
20214
|
const retainedReview = options.discoveryInputs === undefined && policy.bridge?.discovery?.[agent]?.method === "reviewed" ? { ...policy.bridge.discovery[agent], ...agent === "codex" ? { codexDisabledPluginSkills: policy.bridge.codexPluginSkills ?? [] } : {} } : undefined;
|
|
20215
|
-
|
|
20216
|
-
|
|
20217
|
-
|
|
20215
|
+
try {
|
|
20216
|
+
if (retainedReview) {
|
|
20217
|
+
assertRetainedReviewState(agent, { home, dataDir: dataDir2, projectDir: options.projectDir ?? home });
|
|
20218
|
+
retainedAgents.push(agent);
|
|
20219
|
+
}
|
|
20220
|
+
return resolveAgentDiscovery({ home, agent, reviewed: options.discoveryInputs, retainedReview, canonical: (path) => canonicalAgentPath(path, aliases), executable: executable2 });
|
|
20221
|
+
} catch (error) {
|
|
20222
|
+
if (isDiscoveryRootUnresolved(error) || error instanceof SumiPathResolverError || error instanceof NativeSkillCopiesDriftError)
|
|
20223
|
+
throw error;
|
|
20224
|
+
if (!retainedReview)
|
|
20225
|
+
throw error;
|
|
20226
|
+
throw new Error(`NATIVE_SKILL_DRIFT: ${error.message.replace(/^NATIVE_SKILL_DRIFT: /, "")}. This agent reuses a saved discovery review and that review no longer verifies, so a fresh explicit review is required: skills hook install --agent ${agent} --discovery-inputs <review> --apply.`);
|
|
20218
20227
|
}
|
|
20219
|
-
return resolveAgentDiscovery({ home, agent, reviewed: options.discoveryInputs, retainedReview, canonical: (path) => canonicalAgentPath(path, aliases), executable: executable2 });
|
|
20220
20228
|
});
|
|
20221
20229
|
const codexConfigBefore = options.agents.includes("codex") ? readOptional(canonicalAgentPath(join31(home, ".codex", "config.toml"), aliases)) : null;
|
|
20222
20230
|
const codexConfig = Bun.TOML.parse(codexConfigBefore ?? "");
|
|
@@ -20970,7 +20978,7 @@ function applyAgentIntegrationUnlocked(plan, options = {}, assertCurrent) {
|
|
|
20970
20978
|
assertCurrent();
|
|
20971
20979
|
recheckRootAliases(aliases);
|
|
20972
20980
|
for (const agent of plan.retainedReviewChecks?.agents ?? [])
|
|
20973
|
-
|
|
20981
|
+
assertRetainedReviewState(agent, { ...plan.retainedReviewChecks, dataDir: plan.dataDir });
|
|
20974
20982
|
if (plan.reviewArtifactMigration) {
|
|
20975
20983
|
const verified = planReviewedArtifactMigration(plan.reviewArtifactMigration);
|
|
20976
20984
|
if (JSON.stringify(verified.changes) !== JSON.stringify(plan.changes) || JSON.stringify(verified.discoveryAfter) !== JSON.stringify(plan.discoveryAfter))
|
|
@@ -21013,6 +21021,8 @@ function applyAgentIntegrationUnlocked(plan, options = {}, assertCurrent) {
|
|
|
21013
21021
|
if (!plan.changes.length) {
|
|
21014
21022
|
assertCurrent();
|
|
21015
21023
|
recheckRootAliases(aliases);
|
|
21024
|
+
for (const agent of plan.retainedReviewChecks?.agents ?? [])
|
|
21025
|
+
assertManagedAgentBridge(agent, { ...plan.retainedReviewChecks, dataDir: plan.dataDir });
|
|
21016
21026
|
return { changed: [], backups, ...aliases.length ? { rootAliases: aliases } : {} };
|
|
21017
21027
|
}
|
|
21018
21028
|
assertSafePath(backupRoot);
|
|
@@ -21062,6 +21072,8 @@ function applyAgentIntegrationUnlocked(plan, options = {}, assertCurrent) {
|
|
|
21062
21072
|
}
|
|
21063
21073
|
for (const agent of plan.managedAgentChecks?.agents ?? [])
|
|
21064
21074
|
assertManagedAgentBridge(agent, { home: plan.managedAgentChecks.home, dataDir: plan.dataDir, projectDir: plan.managedAgentChecks.projectDir ?? plan.managedAgentChecks.home });
|
|
21075
|
+
for (const agent of plan.retainedReviewChecks?.agents ?? [])
|
|
21076
|
+
assertManagedAgentBridge(agent, { ...plan.retainedReviewChecks, dataDir: plan.dataDir });
|
|
21065
21077
|
atomicWrite2(join31(backupRoot, "receipt.json"), JSON.stringify({ version: 1, changes: written.map((change) => ({ path: change.path, beforeHash: change.before === null ? null : sha4(change.before), afterHash: sha4(change.after) })), backups, ...plan.cacheWitnessUpgrade ? { cacheWitnessUpgrade: plan.cacheWitnessUpgrade } : {}, ...plan.retainedReviewArtifacts?.length ? { retainedReviewArtifacts: plan.retainedReviewArtifacts.map((item) => ({ path: item.path, ...item.artifact })) } : {}, ...aliases.length ? { rootAliases: aliases } : {} }) + `
|
|
21066
21078
|
`);
|
|
21067
21079
|
} catch (error) {
|
|
@@ -21393,7 +21405,7 @@ enabled = false
|
|
|
21393
21405
|
function assertManagedAgentBridge(agent, options = {}) {
|
|
21394
21406
|
return assertManagedAgentBridgeWithDiscovery(agent, options);
|
|
21395
21407
|
}
|
|
21396
|
-
function
|
|
21408
|
+
function assertManagedBridgeCustody(agent, options = {}, provenDiscovery) {
|
|
21397
21409
|
const home = resolve23(options.home ?? homedir2()), dataDir2 = options.dataDir ?? getDataDirReadOnly();
|
|
21398
21410
|
assertSafePath(join31(dataDir2, "agent-policy.json"));
|
|
21399
21411
|
const snapshot2 = readManagedSkillPolicySnapshot(dataDir2);
|
|
@@ -21410,7 +21422,6 @@ function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscov
|
|
|
21410
21422
|
if (!isOwnedCliBridge(expected, [expected]))
|
|
21411
21423
|
throw new Error("NATIVE_SKILL_DRIFT: the native Skills bridge is missing or modified; repair it before continuing");
|
|
21412
21424
|
const roots = projectAncestorDirectories([options.projectDir ?? process.cwd(), ...options.projectDirs ?? [], ...agent === "hermes" && process.env.TERMINAL_CWD ? [process.cwd()] : []]);
|
|
21413
|
-
const visible = (entry) => entry.agent === agent || ["codex", "gemini", "opencode", "hermes", "sumi"].includes(agent) && entry.path.includes(`${sep14}.agents${sep14}skills${sep14}`) || ["opencode", "sumi"].includes(agent) && entry.agent === "claude";
|
|
21414
21425
|
const discovery = provenDiscovery ?? binding.discovery?.[agent];
|
|
21415
21426
|
assertProjectDiscovery(agent, [...roots], home, (path) => canonicalAgentPath(path, aliases), discovery);
|
|
21416
21427
|
for (const source of discovery?.sources ?? [])
|
|
@@ -21425,11 +21436,30 @@ function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscov
|
|
|
21425
21436
|
throw new Error("NATIVE_SKILL_DRIFT: the native hook command/profile binding is missing");
|
|
21426
21437
|
if (options.profileId !== undefined && options.profileId !== profile)
|
|
21427
21438
|
throw new Error("NATIVE_SKILL_DRIFT: the hook selection profile differs from its managed binding; restart the native client after reviewing skills hook install");
|
|
21439
|
+
return { home, dataDir: dataDir2, binding, aliases, expected, roots, discovery, configPath, config, command, profile };
|
|
21440
|
+
}
|
|
21441
|
+
function assertUnrepairableBridgeProtection(custody, agent) {
|
|
21442
|
+
if (agent !== "hermes")
|
|
21443
|
+
return;
|
|
21444
|
+
const { home, dataDir: dataDir2, binding, config, command, profile } = custody;
|
|
21445
|
+
const supervisor = binding.supervisors?.hermes;
|
|
21446
|
+
if (supervisor?.path !== join31(dataDir2, "agent-hooks", "hermes.js") || supervisor?.sha256 !== sha4(renderHermesSupervisor(command, profile)))
|
|
21447
|
+
throw new Error("NATIVE_SKILL_DRIFT: Hermes supervisor binding changed; run skills hook install");
|
|
21448
|
+
assertHermesProtection(home, config, command, profile, supervisor);
|
|
21449
|
+
}
|
|
21450
|
+
function assertRetainedReviewState(agent, options) {
|
|
21451
|
+
const custody = assertManagedBridgeCustody(agent, options);
|
|
21452
|
+
assertUnrepairableBridgeProtection(custody, agent);
|
|
21453
|
+
assertManagedNativeDiscovery(custody, agent, options);
|
|
21454
|
+
}
|
|
21455
|
+
|
|
21456
|
+
class NativeSkillCopiesDriftError extends Error {
|
|
21457
|
+
}
|
|
21458
|
+
function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscovery) {
|
|
21459
|
+
const custody = assertManagedBridgeCustody(agent, options, provenDiscovery);
|
|
21460
|
+
const { home, aliases, expected, discovery, config, command, profile } = custody;
|
|
21428
21461
|
if (agent === "hermes") {
|
|
21429
|
-
|
|
21430
|
-
if (supervisor?.path !== join31(dataDir2, "agent-hooks", "hermes.js") || supervisor?.sha256 !== sha4(renderHermesSupervisor(command, profile)))
|
|
21431
|
-
throw new Error("NATIVE_SKILL_DRIFT: Hermes supervisor binding changed; run skills hook install");
|
|
21432
|
-
assertHermesProtection(home, config, command, profile, supervisor);
|
|
21462
|
+
assertUnrepairableBridgeProtection(custody, agent);
|
|
21433
21463
|
} else if (agent === "sumi") {
|
|
21434
21464
|
if (JSON.stringify(config.permissions?.filter((rule) => rule.action === "skill")) !== JSON.stringify(SUMI_SKILL_PERMISSIONS) || !isManagedSumiPlugin(readOptional(join31(sumiConfigDirectory(home, process.env, options.projectDir ?? process.cwd()), "plugins", "skills-cli.js")), command, profile))
|
|
21435
21465
|
throw new Error("NATIVE_SKILL_DRIFT: Sumi bridge protection changed; run skills hook install");
|
|
@@ -21451,6 +21481,21 @@ function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscov
|
|
|
21451
21481
|
if (agent === "gemini" && (config.hooksConfig?.enabled === false || config.skills?.enabled !== true || !["antigravity-support", "skill-creator"].every((name) => config.skills?.disabled?.includes(name)) || config.skills?.disabled?.includes(CLI_BRIDGE_NAME)))
|
|
21452
21482
|
throw new Error("NATIVE_SKILL_DRIFT: Gemini bridge or bundled-skill protection changed; run skills hook install");
|
|
21453
21483
|
}
|
|
21484
|
+
if (agent === "codex") {
|
|
21485
|
+
const codexConfig = Bun.TOML.parse(readOptional(canonicalAgentPath(join31(home, ".codex", "config.toml"), aliases)) ?? "");
|
|
21486
|
+
if (codexConfig.skills?.bundled?.enabled !== false)
|
|
21487
|
+
throw new Error("NATIVE_SKILL_DRIFT: Codex bundled skill reseeding is not disabled (skills.bundled.enabled); run skills hook install");
|
|
21488
|
+
const setting = (path) => (codexConfig.skills?.config ?? []).filter((entry) => typeof entry.path === "string" && [path, join31(path, "SKILL.md")].includes(canonicalAgentPath(entry.path, aliases)));
|
|
21489
|
+
if ([...setting(expected), ...(codexConfig.skills?.config ?? []).filter((entry) => typeof entry.name === "string" && entry.name.trim() === CLI_BRIDGE_NAME)].some((entry) => entry.enabled === false))
|
|
21490
|
+
throw new Error("NATIVE_SKILL_DRIFT: the Codex CLI bridge is disabled; run skills hook install");
|
|
21491
|
+
}
|
|
21492
|
+
if (agent === "gemini" && discovery && !discovery.builtinNames?.every((name) => config.skills.disabled.includes(name)))
|
|
21493
|
+
throw new Error("NATIVE_SKILL_DRIFT: an installed Gemini builtin is not disabled");
|
|
21494
|
+
return assertManagedNativeDiscovery(custody, agent, options);
|
|
21495
|
+
}
|
|
21496
|
+
function assertManagedNativeDiscovery(custody, agent, options) {
|
|
21497
|
+
const { home, dataDir: dataDir2, binding, aliases, expected, roots, discovery } = custody;
|
|
21498
|
+
const visible = (entry) => entry.agent === agent || ["codex", "gemini", "opencode", "hermes", "sumi"].includes(agent) && entry.path.includes(`${sep14}.agents${sep14}skills${sep14}`) || ["opencode", "sumi"].includes(agent) && entry.agent === "claude";
|
|
21454
21499
|
const codexPath = canonicalAgentPath(join31(home, ".codex", "config.toml"), aliases);
|
|
21455
21500
|
const vendorCache = canonicalAgentPath(join31(home, ".codex", "plugins", "cache"), aliases);
|
|
21456
21501
|
const witnessTolerance = agent === "codex" && options.codexPromptHook === true ? {
|
|
@@ -21461,11 +21506,6 @@ function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscov
|
|
|
21461
21506
|
if (options.codexDiscoveryRecovery && (agent !== "codex" || options.codexDiscoveryRecovery.configPath !== codexPath))
|
|
21462
21507
|
throw new Error("NATIVE_SKILL_DRIFT: Codex trust recovery names a different configuration root");
|
|
21463
21508
|
const codexConfig = agent === "codex" ? Bun.TOML.parse(readOptional(codexPath) ?? "") : {};
|
|
21464
|
-
if (agent === "codex" && codexConfig.skills?.bundled?.enabled !== false)
|
|
21465
|
-
throw new Error("NATIVE_SKILL_DRIFT: Codex bundled skill reseeding is not disabled (skills.bundled.enabled); run skills hook install");
|
|
21466
|
-
const setting = (path) => (codexConfig.skills?.config ?? []).filter((entry) => typeof entry.path === "string" && [path, join31(path, "SKILL.md")].includes(canonicalAgentPath(entry.path, aliases)));
|
|
21467
|
-
if (agent === "codex" && [...setting(expected), ...(codexConfig.skills?.config ?? []).filter((entry) => typeof entry.name === "string" && entry.name.trim() === CLI_BRIDGE_NAME)].some((entry) => entry.enabled === false))
|
|
21468
|
-
throw new Error("NATIVE_SKILL_DRIFT: the Codex CLI bridge is disabled; run skills hook install");
|
|
21469
21509
|
if (agent === "codex" && !reviewedCodexPluginCapabilitiesUnchanged(canonicalAgentPath(join31(home, ".codex/plugins/cache"), aliases), binding.codexPluginSkills ?? [], (path) => new TextDecoder("utf-8", { fatal: true }).decode(readNativeBytes(path, 1024 * 1024)), codexConfig.skills?.config ?? []))
|
|
21470
21510
|
throw new Error("NATIVE_SKILL_DRIFT: reviewed native plugin capability controls changed; run skills hook install with a fresh discovery review");
|
|
21471
21511
|
const disabledPlugin = (entry) => {
|
|
@@ -21502,8 +21542,6 @@ function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscov
|
|
|
21502
21542
|
};
|
|
21503
21543
|
if (!discovery || discovery.agent !== agent)
|
|
21504
21544
|
throw new Error("NATIVE_SKILL_DRIFT: native discovery coverage is missing; run skills hook install");
|
|
21505
|
-
if (agent === "gemini" && !discovery.builtinNames?.every((name) => config.skills.disabled.includes(name)))
|
|
21506
|
-
throw new Error("NATIVE_SKILL_DRIFT: an installed Gemini builtin is not disabled");
|
|
21507
21545
|
try {
|
|
21508
21546
|
verifyAgentDiscovery(agent === "codex" ? { ...discovery, codexDisabledPluginSkills: binding.codexPluginSkills ?? [] } : discovery, options.codexDiscoveryRecovery, aliases, witnessTolerance);
|
|
21509
21547
|
if (agent === "sumi" && discovery.method === "reviewed")
|
|
@@ -21558,7 +21596,7 @@ function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscov
|
|
|
21558
21596
|
return false;
|
|
21559
21597
|
}
|
|
21560
21598
|
}, read: (path) => new TextDecoder("utf-8", { fatal: true }).decode(readNativeBytes(path, 1024 * 1024)) });
|
|
21561
|
-
throw new
|
|
21599
|
+
throw new NativeSkillCopiesDriftError(`NATIVE_SKILL_DRIFT: ${blocking.length} unexpected native skill copies were found: ${nativeSkillPathList(blocking.map((entry) => entry.path))}. ${remediation}${adapterRefusal ? ` Native policy adapter refused: ${adapterRefusal}` : ""}`);
|
|
21562
21600
|
}
|
|
21563
21601
|
recheckRootAliases(aliases);
|
|
21564
21602
|
const discoveryChanges = (witnessTolerance?.changes ?? []).filter((change, index, all) => all.findIndex((other) => other.kind === change.kind && other.path === change.path) === index);
|
|
@@ -24133,6 +24171,12 @@ function verifyAgentDiscovery(binding, codexRecovery, reviewedRootAliases, witne
|
|
|
24133
24171
|
}
|
|
24134
24172
|
var CLAUDE_CONTROL_WITNESS_MODES = new Set(["claude-settings-v1", "claude-settings-v2", "claude-settings-v3", "claude-settings-v4", "claude-marketplace-registry", "claude-marketplace-registry-v2", "claude-marketplace-entry-v1", "claude-plugin-registry"]);
|
|
24135
24173
|
var CLAUDE_CONTROL_FILES = new Set(["settings.json", "settings.local.json", "installed_plugins.json", "known_marketplaces.json"]);
|
|
24174
|
+
function claudeControlSource(source) {
|
|
24175
|
+
return CLAUDE_CONTROL_WITNESS_MODES.has(source.hashMode ?? "") || CLAUDE_CONTROL_FILES.has(basename15(source.path)) && !source.path.includes(`${sep15}.claude${sep15}plugins${sep15}cache${sep15}`);
|
|
24176
|
+
}
|
|
24177
|
+
function retainedAbsentClaudeRoot(absentRoots, source) {
|
|
24178
|
+
return claudeControlSource(source) ? undefined : retainedAbsentRoot(absentRoots, source);
|
|
24179
|
+
}
|
|
24136
24180
|
function codexInstallationRoots(binding) {
|
|
24137
24181
|
const proof = binding.codexInstallationInputs;
|
|
24138
24182
|
if (proof === undefined)
|
|
@@ -24542,7 +24586,10 @@ function resolveAgentDiscovery(options) {
|
|
|
24542
24586
|
if (!review.sources.some((source) => source.path === canonical9(configPath)))
|
|
24543
24587
|
throw new Error("Discovery review must include the agent configuration source");
|
|
24544
24588
|
const reviewedSources = supersedesSettingsProjection(agent, review.sources, canonical9(configPath)) ? sources.filter((source) => !isSupersededSettingsProjection(agent, canonical9(configPath), source)) : sources;
|
|
24589
|
+
const absentRoots = absentRetiredClaudeRoots(supplied);
|
|
24545
24590
|
const reviewedSourcesWithManifestProjection = review.sources.map((source) => {
|
|
24591
|
+
if (retainedAbsentClaudeRoot(absentRoots, source) !== undefined)
|
|
24592
|
+
return source;
|
|
24546
24593
|
if (agent === "claude" && source.format === undefined && source.fields === undefined && source.managedPlugins === undefined && source.reviewArtifact === undefined && [undefined, "bytes", "path-bytes"].includes(source.hashMode) && isClaudePluginCacheOrphanMarker(source.path)) {
|
|
24547
24594
|
return { path: source.path, hashMode: CLAUDE_ORPHAN_MARKER_MODE, sha256: hashClaudePluginCacheOrphanMarker(source.path) };
|
|
24548
24595
|
}
|
|
@@ -24575,6 +24622,11 @@ function resolveAgentDiscovery(options) {
|
|
|
24575
24622
|
});
|
|
24576
24623
|
if (agent === "claude")
|
|
24577
24624
|
assertReviewedClaudeCacheClosures(home, config, review, canonical9);
|
|
24625
|
+
for (const root2 of absentRoots) {
|
|
24626
|
+
safe6(root2);
|
|
24627
|
+
if (lstatSync32(root2, { throwIfNoEntry: false }))
|
|
24628
|
+
throw new Error("Retired native materialization changed during discovery review");
|
|
24629
|
+
}
|
|
24578
24630
|
const combined = [...reviewedSources, ...reviewedSourcesWithManifestProjection];
|
|
24579
24631
|
const unique = combined.filter((source, index) => combined.findIndex((candidate2) => isDeepStrictEqual9(candidate2, source)) === index);
|
|
24580
24632
|
return { ...supplied, roots: [...new Set([...roots, ...supplied.roots])].sort(), sources: unique, ...agent === "gemini" ? { builtinNames } : {} };
|
|
@@ -52,6 +52,16 @@ export declare function ensurePortableSkillFiles(skillPath: string, manifest: Po
|
|
|
52
52
|
* locally, so `port` must never fabricate executable stubs (package.json, bin,
|
|
53
53
|
* src/index.ts, tsconfig.json, AGENTS.md). Apart from aligning declared names
|
|
54
54
|
* after an import rename, it preserves the copied prose and metadata.
|
|
55
|
+
*
|
|
56
|
+
* "Metadata" includes what the candidate declared in skill.json. An instruction
|
|
57
|
+
* skill may legitimately bundle a helper command (and inputs for it) — the
|
|
58
|
+
* validator permits a package.json bin on an instruction skill for exactly that
|
|
59
|
+
* reason — so declared `commands` and `inputs` are carried through verbatim and
|
|
60
|
+
* only an absent declaration becomes an explicit empty array. Forcing them to []
|
|
61
|
+
* here silently erased the declaration on disk: writeSkillJsonWithHash() merges
|
|
62
|
+
* the rebuilt manifest over the copied candidate skill.json, so the candidate's
|
|
63
|
+
* own array was destroyed and the manifest was left self-inconsistent
|
|
64
|
+
* (runtime.entrypoint still naming an entry no command declared).
|
|
55
65
|
*/
|
|
56
66
|
export declare function ensureInstructionSkillFiles(skillPath: string, manifest: PortableSkillManifest): PortableSkillManifest;
|
|
57
67
|
export declare function copySkillDirectory(source: string, destination: string): void;
|
|
@@ -13,6 +13,50 @@ export interface ResolvedSelectedRun {
|
|
|
13
13
|
cacheDir: string;
|
|
14
14
|
}
|
|
15
15
|
export declare function resolveSelectedRun(spec: string, profileId: string, options?: SelectionResolverOptions): Promise<ResolvedSelectedRun>;
|
|
16
|
+
/**
|
|
17
|
+
* The operation an instruction skill declares, if it declares one.
|
|
18
|
+
*
|
|
19
|
+
* An instruction skill is prose-first: agents follow SKILL.md, and `skills run`
|
|
20
|
+
* refuses it. Its manifest may still declare the one operation it ships — the
|
|
21
|
+
* first `commands` entry, naming the helper — together with an explicit runtime
|
|
22
|
+
* contract in `runtime` (`runtime` plus its `version`/`timeout` policy). Such a
|
|
23
|
+
* skill is runnable through that declared operation, and only through it: the
|
|
24
|
+
* executed file is exactly the entry the selected command declares, it must be a
|
|
25
|
+
* regular file in the selected bundle, and a `runtime.entrypoint` that disagrees
|
|
26
|
+
* with it is an inconsistent declaration that refuses (see
|
|
27
|
+
* {@link instructionEntrypointMismatch}) rather than quietly running the other
|
|
28
|
+
* file. An instruction skill that declares no operation — no command, a
|
|
29
|
+
* non-string or empty entry, or no runtime contract — keeps the prose-only
|
|
30
|
+
* refusal (`skills load`, never `skills run`), so an instruction bundle is never
|
|
31
|
+
* mistaken for a runnable stub.
|
|
32
|
+
*/
|
|
33
|
+
export declare function declaredOperation(manifest: Record<string, any>): {
|
|
34
|
+
entry: string;
|
|
35
|
+
} | null;
|
|
36
|
+
/**
|
|
37
|
+
* The entry the instruction skill's declared command names, when it names one.
|
|
38
|
+
* The first `commands` entry is the operation: nothing else in the manifest
|
|
39
|
+
* selects what an instruction skill runs.
|
|
40
|
+
*/
|
|
41
|
+
export declare function instructionCommandEntry(manifest: Record<string, any>): string | null;
|
|
42
|
+
/**
|
|
43
|
+
* A declared `runtime.entrypoint` that disagrees with the declared command entry,
|
|
44
|
+
* for an instruction skill. The command entry is the authority, so a disagreement
|
|
45
|
+
* is an inconsistent declaration: it refuses, and the disagreeing file never runs.
|
|
46
|
+
*/
|
|
47
|
+
export declare function instructionEntrypointMismatch(manifest: Record<string, any>): string | null;
|
|
48
|
+
/**
|
|
49
|
+
* Why an instruction skill cannot run locally, if it cannot. `isBundledFile` says
|
|
50
|
+
* whether a path is a regular file in the selected bundle, so a declared command
|
|
51
|
+
* entry that is not there is refused by name instead of being substituted. The
|
|
52
|
+
* CLI, the MCP tool and the local executor all ask this one function, so the
|
|
53
|
+
* reason and the code cannot drift between them. A runnable instruction skill
|
|
54
|
+
* (declared command, agreeing runtime, entry present in the bundle) returns null.
|
|
55
|
+
*/
|
|
56
|
+
export declare function instructionRunRefusal(manifest: Record<string, any>, isBundledFile: (path: string) => boolean): {
|
|
57
|
+
code: string;
|
|
58
|
+
message: string;
|
|
59
|
+
} | null;
|
|
16
60
|
export interface SelectedLocalRunOptions {
|
|
17
61
|
args?: string[];
|
|
18
62
|
input?: unknown;
|
|
@@ -54,6 +98,7 @@ export declare function executeSelectedLocal(selected: ResolvedSelectedRun, opti
|
|
|
54
98
|
selection: ResolvedSkillSelection;
|
|
55
99
|
target: "local";
|
|
56
100
|
runtime: any;
|
|
101
|
+
entrypoint: string;
|
|
57
102
|
inputDigest: string;
|
|
58
103
|
runDirectory: string;
|
|
59
104
|
}>;
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { type SelectionCacheOptions } from "./selection-cache.js";
|
|
2
|
+
/** Bounded so one receipt can never carry an unbounded drift list. */
|
|
3
|
+
export declare const PROVENANCE_DRIFT_LIMIT = 200;
|
|
4
|
+
export type SelectionProvenanceStatus = "current" | "behind" | "pinned" | "unverified";
|
|
5
|
+
export type SelectionProvenanceSource = "local-receipt" | "authority-receipt" | "session-receipt";
|
|
6
|
+
export type SelectionProvenanceReference = "authority" | "synced-snapshot" | "unavailable";
|
|
7
|
+
/**
|
|
8
|
+
* The fixed diagnostic vocabulary. A reader's exception text is never carried,
|
|
9
|
+
* only one of these codes, so a foreign body or a credential inside an error
|
|
10
|
+
* string cannot reach a receipt that may be shared.
|
|
11
|
+
*/
|
|
12
|
+
export type SelectionProvenanceReason = "CURRENT_SCHEMA_MATCH" | "REVISION_BEHIND" | "PINNED_BY_DESIGN" | "IDENTITY_MISMATCH" | "INCOMPLETE_RECEIPT" | "DIFFERENT_PROFILE" | "NO_LOCAL_RECEIPT" | "NO_SESSION_NAMED" | "NO_SESSION_RECEIPT" | "NO_STATION_RECEIPT" | "READ_REFUSED" | "REFERENCE_FROM_AUTHORITY" | "REFERENCE_NOT_AUTHORITY" | "REFERENCE_UNAVAILABLE" | "NO_STATION_REPORTED";
|
|
13
|
+
export interface SelectionProvenanceVersion {
|
|
14
|
+
slug: string;
|
|
15
|
+
version: string;
|
|
16
|
+
bundleDigest?: string;
|
|
17
|
+
}
|
|
18
|
+
export interface SelectionProvenanceConsumer {
|
|
19
|
+
kind: "station" | "session";
|
|
20
|
+
source: SelectionProvenanceSource;
|
|
21
|
+
/** The local host for a local receipt, the requested station only for the authority receipt. */
|
|
22
|
+
id: string;
|
|
23
|
+
/** Whether `id` names the host running this command or a station named on the command line. */
|
|
24
|
+
idKind: "local-host" | "requested-station";
|
|
25
|
+
profileId?: string;
|
|
26
|
+
profileRevision?: string;
|
|
27
|
+
/** Compared, never assumed: a matching revision under another identity is not current. */
|
|
28
|
+
authority?: string;
|
|
29
|
+
workspaceId?: string;
|
|
30
|
+
observedAt?: string;
|
|
31
|
+
selectionCount?: number;
|
|
32
|
+
/** Only for a session: how many of its selections it really loaded. */
|
|
33
|
+
loadedCount?: number;
|
|
34
|
+
/** Only for a session: the exact versions it really loaded. */
|
|
35
|
+
consumed?: SelectionProvenanceVersion[];
|
|
36
|
+
status: SelectionProvenanceStatus;
|
|
37
|
+
reasonCode: SelectionProvenanceReason;
|
|
38
|
+
reason: string;
|
|
39
|
+
}
|
|
40
|
+
export interface SelectionProvenanceDrift {
|
|
41
|
+
source: SelectionProvenanceSource;
|
|
42
|
+
id: string;
|
|
43
|
+
slug: string;
|
|
44
|
+
selectedVersion: string;
|
|
45
|
+
observedVersion: string;
|
|
46
|
+
}
|
|
47
|
+
export interface SelectionProvenanceReceipt {
|
|
48
|
+
schemaVersion: 1;
|
|
49
|
+
readOnly: true;
|
|
50
|
+
selectionProfile: string;
|
|
51
|
+
selected: {
|
|
52
|
+
profileId: string;
|
|
53
|
+
profileRevision?: string;
|
|
54
|
+
selectionCount?: number;
|
|
55
|
+
authority?: string;
|
|
56
|
+
workspaceId?: string;
|
|
57
|
+
source: SelectionProvenanceReference;
|
|
58
|
+
observedAt?: string;
|
|
59
|
+
reasonCode: SelectionProvenanceReason;
|
|
60
|
+
reason: string;
|
|
61
|
+
};
|
|
62
|
+
consumers: SelectionProvenanceConsumer[];
|
|
63
|
+
drift: SelectionProvenanceDrift[];
|
|
64
|
+
driftTruncated: boolean;
|
|
65
|
+
/**
|
|
66
|
+
* What this receipt deliberately does not attest. Version and digest evidence
|
|
67
|
+
* for published payloads, native discovery, native locks and live consumer
|
|
68
|
+
* adoption are outside this command and stay unverified here rather than being
|
|
69
|
+
* implied by a clean-looking result.
|
|
70
|
+
*/
|
|
71
|
+
notAttested: string[];
|
|
72
|
+
}
|
|
73
|
+
export interface SelectionProvenanceInput {
|
|
74
|
+
profileId: string;
|
|
75
|
+
/** The station whose authority-held sync receipt is read. */
|
|
76
|
+
stationId: string;
|
|
77
|
+
/** The host this command runs on. Never conflated with `stationId`. */
|
|
78
|
+
localStationId: string;
|
|
79
|
+
sessionId?: string;
|
|
80
|
+
/** Read the reference from the locally synced snapshot; never ask the authority. */
|
|
81
|
+
cached?: boolean;
|
|
82
|
+
}
|
|
83
|
+
/** A station receipt reduced to what this receipt compares. */
|
|
84
|
+
export interface ProvenanceStationObservation {
|
|
85
|
+
profileId: string;
|
|
86
|
+
profileRevision: string;
|
|
87
|
+
appliedAt: string;
|
|
88
|
+
workspaceId?: string;
|
|
89
|
+
selections: readonly {
|
|
90
|
+
slug: string;
|
|
91
|
+
version: string;
|
|
92
|
+
}[];
|
|
93
|
+
}
|
|
94
|
+
export interface SelectionProvenanceOptions extends SelectionCacheOptions {
|
|
95
|
+
/** Injected by the SDK and tests; defaults to the authenticated reads. */
|
|
96
|
+
readSelected?: (profileId: string) => Promise<{
|
|
97
|
+
profileId: string;
|
|
98
|
+
profileRevision: string;
|
|
99
|
+
authority?: string;
|
|
100
|
+
workspaceId?: string;
|
|
101
|
+
selections: readonly {
|
|
102
|
+
slug: string;
|
|
103
|
+
version: string;
|
|
104
|
+
}[];
|
|
105
|
+
}>;
|
|
106
|
+
readStation?: (stationId: string) => Promise<ProvenanceStationObservation>;
|
|
107
|
+
}
|
|
108
|
+
export declare function readSelectionProvenance(input: SelectionProvenanceInput, options?: SelectionProvenanceOptions): Promise<SelectionProvenanceReceipt>;
|
|
@@ -49,7 +49,7 @@ var __require = import.meta.require;
|
|
|
49
49
|
// src/runtime-prerequisites.ts
|
|
50
50
|
import { isAbsolute as isAbsolute7 } from "path";
|
|
51
51
|
// package.json
|
|
52
|
-
var version = "0.10.
|
|
52
|
+
var version = "0.10.74";
|
|
53
53
|
var package_default = {
|
|
54
54
|
name: "@hasna/skills",
|
|
55
55
|
version,
|