@hasna/skills 0.10.64 → 0.10.66
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 +7 -1
- package/bin/index.js +511 -211
- package/bin/mcp.js +335 -40
- package/bin/migrate.js +1 -1
- package/bin/server.js +1 -1
- package/bin/worker.js +1 -1
- package/dist/index.js +426 -61
- package/dist/lib/agent-discovery.d.ts +3 -2
- package/dist/lib/agent-integration.d.ts +2 -0
- package/dist/lib/codex-semantic-cache-witness.d.ts +23 -0
- package/dist/lib/hook-diagnostics.d.ts +2 -0
- package/dist/runtime-prerequisites.js +133 -14
- package/dist/sdk/index.js +437 -72
- package/package.json +1 -1
|
@@ -4,7 +4,7 @@ import { type ManagedPluginRegistrationWitness } from "./plugin-discovery.js";
|
|
|
4
4
|
import { type CodexHookDiscoveryRecovery } from "./codex-hook-discovery-recovery.js";
|
|
5
5
|
import { type CodexPluginSkillControl, type CodexPluginSourceInput } from "./codex-plugin-skill-controls.js";
|
|
6
6
|
import { type RetainedReviewArtifact, type ReviewedArtifact } from "./retained-review-artifacts.js";
|
|
7
|
-
import { type CodexSemanticCacheWitness, type CodexSemanticCacheAppOnlyParent } from "./codex-semantic-cache-witness.js";
|
|
7
|
+
import { type CodexSemanticCacheWitness, type CodexSemanticCacheAppOnlyParent, type CodexSemanticCacheReviewedHookParent } from "./codex-semantic-cache-witness.js";
|
|
8
8
|
export { captureDiscoveryDirectories, type DiscoveryDirectory } from "./agent-discovery-directories.js";
|
|
9
9
|
export interface DiscoverySource {
|
|
10
10
|
reviewArtifact?: RetainedReviewArtifact;
|
|
@@ -26,6 +26,7 @@ export interface CodexSemanticCacheDiscovery {
|
|
|
26
26
|
} | null;
|
|
27
27
|
witness: CodexSemanticCacheWitness;
|
|
28
28
|
appOnlyParents: CodexSemanticCacheAppOnlyParent[];
|
|
29
|
+
reviewedHookParents?: CodexSemanticCacheReviewedHookParent[];
|
|
29
30
|
previousPolicySha256: string;
|
|
30
31
|
supersededSources: DiscoverySource[];
|
|
31
32
|
supersededDirectories: DiscoveryDirectory[];
|
|
@@ -93,7 +94,7 @@ export declare function projectNativeDiscoveryFields(text: string, format: "json
|
|
|
93
94
|
export declare function captureRetiredCodexDiscovery(binding: AgentDiscoveryBinding, controls: CodexPluginSkillControl[], rules: unknown): AgentDiscoveryBinding;
|
|
94
95
|
/** Only native capability locations covered by the complete typed cache census
|
|
95
96
|
* qualify. Historical sources remain stored; arbitrary missing files do not. */
|
|
96
|
-
export declare function codexSemanticCacheSourceRole(source: DiscoverySource, cacheRoot: string, parents: readonly string[]): boolean;
|
|
97
|
+
export declare function codexSemanticCacheSourceRole(source: DiscoverySource, cacheRoot: string, parents: readonly string[], hookParents?: readonly string[]): boolean;
|
|
97
98
|
export declare function codexSemanticCacheDirectoryRole(path: string, cacheRoot: string): boolean;
|
|
98
99
|
export declare function assertCodexSemanticCacheHookSafety(cacheRoot: string, parents: readonly string[], configPath: string, hookRootAlias?: CodexSemanticCacheDiscovery["hookRootAlias"]): void;
|
|
99
100
|
export declare function verifyAgentDiscovery(binding: AgentDiscoveryBinding, codexRecovery?: CodexHookDiscoveryRecovery, reviewedRootAliases?: readonly {
|
|
@@ -215,6 +215,8 @@ export declare function planAgentSettingsWitnessUpgrade(options: {
|
|
|
215
215
|
export interface CodexSemanticCacheUpgrade {
|
|
216
216
|
agent: "codex";
|
|
217
217
|
appOnlyParents: string[];
|
|
218
|
+
/** Explicit cache parents whose complete native mcp_tool hook contract was already enrolled. */
|
|
219
|
+
reviewedHookParents?: string[];
|
|
218
220
|
expectedPolicySha256: string;
|
|
219
221
|
expectedSettingsSha256: string;
|
|
220
222
|
expectedCacheWitnessSha256?: string;
|
|
@@ -32,11 +32,31 @@ export interface CodexSemanticCacheAppOnlyParentWitness extends CodexSemanticCac
|
|
|
32
32
|
appSha256: string;
|
|
33
33
|
mcpSha256?: string;
|
|
34
34
|
}
|
|
35
|
+
/** Explicitly reviewed native MCP-hook parent. Its cache materializations are
|
|
36
|
+
* admitted only when the complete manifest and hooks declaration were already
|
|
37
|
+
* enrolled as full-byte native discovery sources. */
|
|
38
|
+
export interface CodexSemanticCacheReviewedHookParent {
|
|
39
|
+
role: "reviewed-hook";
|
|
40
|
+
pluginId: string;
|
|
41
|
+
namespace: string;
|
|
42
|
+
pluginParent: string;
|
|
43
|
+
installation: "remote-receipt" | "local-marketplace";
|
|
44
|
+
remotePluginId?: string;
|
|
45
|
+
marketplaceSourcePath?: string;
|
|
46
|
+
marketplaceSha256?: string;
|
|
47
|
+
}
|
|
48
|
+
export interface CodexSemanticCacheReviewedHookParentWitness extends CodexSemanticCacheReviewedHookParent {
|
|
49
|
+
manifestSha256: string;
|
|
50
|
+
hooksSha256: string;
|
|
51
|
+
receiptSha256?: string;
|
|
52
|
+
}
|
|
35
53
|
export interface CodexSemanticCacheWitness {
|
|
36
54
|
schema: typeof CODEX_SEMANTIC_CACHE_WITNESS_SCHEMA;
|
|
37
55
|
cacheRoot: string;
|
|
38
56
|
controls: CodexSemanticCacheControl[];
|
|
39
57
|
appOnlyParents: CodexSemanticCacheAppOnlyParentWitness[];
|
|
58
|
+
/** Optional only for compatibility with already persisted v1 witnesses. */
|
|
59
|
+
hookParents?: CodexSemanticCacheReviewedHookParentWitness[];
|
|
40
60
|
skills: CodexSemanticCacheSkill[];
|
|
41
61
|
sha256: string;
|
|
42
62
|
}
|
|
@@ -45,10 +65,13 @@ export interface CaptureCodexSemanticCacheWitnessOptions {
|
|
|
45
65
|
controls: CodexPluginSkillControl[];
|
|
46
66
|
/** Explicit package-reviewed parents; absence never infers this role. */
|
|
47
67
|
appOnlyParents?: CodexSemanticCacheAppOnlyParent[];
|
|
68
|
+
/** Explicit roles only; cache presence never creates a hook role. */
|
|
69
|
+
reviewedHookParents?: CodexSemanticCacheReviewedHookParent[];
|
|
48
70
|
rules: unknown;
|
|
49
71
|
/** Package-owned, bounded read used by the existing semantic control review. */
|
|
50
72
|
read?: (path: string) => string;
|
|
51
73
|
}
|
|
74
|
+
export declare function codexLocalMarketplaceMapsPlugin(text: string, marketplace: string, plugin: string): boolean;
|
|
52
75
|
/** Capture the current semantic identity without changing the stored baseline.
|
|
53
76
|
* Existing package-owned parsers validate documents and capability controls. */
|
|
54
77
|
export declare function captureCodexSemanticCacheWitness(options: CaptureCodexSemanticCacheWitnessOptions): CodexSemanticCacheWitness;
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
/** Fixed codes only; generated native adapters must never copy refusal prose. */
|
|
2
|
+
export declare const HOOK_REFUSAL_REASON_CODES: readonly string[];
|
|
1
3
|
export declare class HookDiagnosticError extends Error {
|
|
2
4
|
readonly code: string;
|
|
3
5
|
readonly stage: "context" | "sync";
|
|
@@ -49,7 +49,7 @@ var __require = import.meta.require;
|
|
|
49
49
|
// src/runtime-prerequisites.ts
|
|
50
50
|
import { isAbsolute as isAbsolute5 } from "path";
|
|
51
51
|
// package.json
|
|
52
|
-
var version = "0.10.
|
|
52
|
+
var version = "0.10.66";
|
|
53
53
|
var package_default = {
|
|
54
54
|
name: "@hasna/skills",
|
|
55
55
|
version,
|
|
@@ -2643,8 +2643,8 @@ function assertAgentPolicyCollections(policy) {
|
|
|
2643
2643
|
}
|
|
2644
2644
|
}
|
|
2645
2645
|
if (value.codexSemanticCache !== undefined) {
|
|
2646
|
-
const proof = record(value.codexSemanticCache,
|
|
2647
|
-
requireBound(agent === "codex" && value.agent === "codex" && value.method === "reviewed" && Object.keys(proof).length
|
|
2646
|
+
const proof = record(value.codexSemanticCache, 9), witness = record(proof.witness, 7);
|
|
2647
|
+
requireBound(agent === "codex" && value.agent === "codex" && value.method === "reviewed" && [8, 9].includes(Object.keys(proof).length) && Object.keys(proof).every((key) => ["schema", "configPath", "hookRootAlias", "witness", "appOnlyParents", "previousPolicySha256", "supersededSources", "supersededDirectories", "reviewedHookParents"].includes(key)) && proof.schema === "hasna.codex-semantic-cache-discovery.v1" && typeof proof.previousPolicySha256 === "string" && /^[a-f0-9]{64}$/.test(proof.previousPolicySha256) && [6, 7].includes(Object.keys(witness).length) && Object.keys(witness).every((key) => ["schema", "cacheRoot", "controls", "appOnlyParents", "skills", "sha256", "hookParents"].includes(key)) && witness.schema === "hasna.codex-semantic-cache-witness.v1" && typeof witness.sha256 === "string" && /^[a-f0-9]{64}$/.test(witness.sha256));
|
|
2648
2648
|
text(proof.configPath);
|
|
2649
2649
|
requireBound(isAbsolute3(proof.configPath) && resolve5(proof.configPath) === proof.configPath && basename3(proof.configPath) === "config.toml");
|
|
2650
2650
|
text(witness.cacheRoot);
|
|
@@ -2663,6 +2663,20 @@ function assertAgentPolicyCollections(policy) {
|
|
|
2663
2663
|
text(parent[key]);
|
|
2664
2664
|
requireBound(isAbsolute3(parent.pluginParent) && resolve5(parent.pluginParent) === parent.pluginParent);
|
|
2665
2665
|
}
|
|
2666
|
+
if (proof.reviewedHookParents !== undefined)
|
|
2667
|
+
for (const parent of array(proof.reviewedHookParents, 4096)) {
|
|
2668
|
+
requireBound(object(parent) && parent.role === "reviewed-hook" && (parent.installation === "remote-receipt" && Object.keys(parent).length === 6 || parent.installation === "local-marketplace" && Object.keys(parent).length === 7));
|
|
2669
|
+
requireBound(parent.installation === "remote-receipt" ? Object.keys(parent).sort().join(",") === "installation,namespace,pluginId,pluginParent,remotePluginId,role" : Object.keys(parent).sort().join(",") === "installation,marketplaceSha256,marketplaceSourcePath,namespace,pluginId,pluginParent,role");
|
|
2670
|
+
for (const key of ["pluginId", "namespace", "pluginParent", "installation"])
|
|
2671
|
+
text(parent[key]);
|
|
2672
|
+
if (parent.installation === "remote-receipt")
|
|
2673
|
+
text(parent.remotePluginId);
|
|
2674
|
+
else {
|
|
2675
|
+
text(parent.marketplaceSourcePath);
|
|
2676
|
+
requireBound(typeof parent.marketplaceSha256 === "string" && /^[a-f0-9]{64}$/.test(parent.marketplaceSha256) && isAbsolute3(parent.marketplaceSourcePath) && resolve5(parent.marketplaceSourcePath) === parent.marketplaceSourcePath && basename3(parent.marketplaceSourcePath) === "marketplace.json");
|
|
2677
|
+
}
|
|
2678
|
+
requireBound(isAbsolute3(parent.pluginParent) && resolve5(parent.pluginParent) === parent.pluginParent);
|
|
2679
|
+
}
|
|
2666
2680
|
for (const row of [...array(witness.controls, 4096), ...array(witness.appOnlyParents, 4096)]) {
|
|
2667
2681
|
requireBound(object(row));
|
|
2668
2682
|
for (const key of ["pluginId", "namespace", "pluginParent"])
|
|
@@ -2671,6 +2685,26 @@ function assertAgentPolicyCollections(policy) {
|
|
|
2671
2685
|
if (row[key] !== undefined)
|
|
2672
2686
|
requireBound(typeof row[key] === "string" && /^[a-f0-9]{64}$/.test(row[key]));
|
|
2673
2687
|
}
|
|
2688
|
+
if (witness.hookParents !== undefined)
|
|
2689
|
+
for (const row of array(witness.hookParents, 4096)) {
|
|
2690
|
+
requireBound(object(row) && row.role === "reviewed-hook" && (row.installation === "remote-receipt" && Object.keys(row).length === 9 || row.installation === "local-marketplace" && Object.keys(row).length === 9));
|
|
2691
|
+
requireBound(row.installation === "remote-receipt" ? Object.keys(row).sort().join(",") === "hooksSha256,installation,manifestSha256,namespace,pluginId,pluginParent,receiptSha256,remotePluginId,role" : Object.keys(row).sort().join(",") === "hooksSha256,installation,manifestSha256,marketplaceSha256,marketplaceSourcePath,namespace,pluginId,pluginParent,role");
|
|
2692
|
+
for (const key of ["pluginId", "namespace", "pluginParent", "installation"])
|
|
2693
|
+
text(row[key]);
|
|
2694
|
+
if (row.installation === "remote-receipt")
|
|
2695
|
+
text(row.remotePluginId);
|
|
2696
|
+
else {
|
|
2697
|
+
text(row.marketplaceSourcePath);
|
|
2698
|
+
requireBound(typeof row.marketplaceSha256 === "string" && /^[a-f0-9]{64}$/.test(row.marketplaceSha256) && isAbsolute3(row.marketplaceSourcePath) && resolve5(row.marketplaceSourcePath) === row.marketplaceSourcePath && basename3(row.marketplaceSourcePath) === "marketplace.json");
|
|
2699
|
+
}
|
|
2700
|
+
requireBound(isAbsolute3(row.pluginParent) && resolve5(row.pluginParent) === row.pluginParent);
|
|
2701
|
+
for (const key of ["manifestSha256", "hooksSha256"])
|
|
2702
|
+
requireBound(typeof row[key] === "string" && /^[a-f0-9]{64}$/.test(row[key]));
|
|
2703
|
+
if (row.installation === "remote-receipt")
|
|
2704
|
+
requireBound(typeof row.receiptSha256 === "string" && /^[a-f0-9]{64}$/.test(row.receiptSha256));
|
|
2705
|
+
else
|
|
2706
|
+
requireBound(row.receiptSha256 === undefined);
|
|
2707
|
+
}
|
|
2674
2708
|
for (const skill of array(witness.skills, 4096)) {
|
|
2675
2709
|
requireBound(object(skill));
|
|
2676
2710
|
for (const key of ["name", "pluginId", "namespace", "pluginParent"])
|
|
@@ -2864,6 +2898,78 @@ function detectedIntegrationAgents(options = {}) {
|
|
|
2864
2898
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
2865
2899
|
import { accessSync, constants as constants4, existsSync as existsSync4, lstatSync as lstatSync5, realpathSync, statSync as statSync2 } from "fs";
|
|
2866
2900
|
import { delimiter, isAbsolute as isAbsolute4, join as join8, resolve as resolve7 } from "path";
|
|
2901
|
+
|
|
2902
|
+
// src/lib/hook-diagnostics.ts
|
|
2903
|
+
var CONTEXT_ERROR_CODES = new Set([
|
|
2904
|
+
"SKILLS_API_CREDENTIAL_UNAVAILABLE",
|
|
2905
|
+
"SKILLS_API_UNAVAILABLE",
|
|
2906
|
+
"SKILLS_API_UNAUTHORIZED",
|
|
2907
|
+
"SKILLS_API_FORBIDDEN",
|
|
2908
|
+
"SKILLS_API_RESOURCE_UNAVAILABLE",
|
|
2909
|
+
"SESSION_WRITE_BUSY",
|
|
2910
|
+
"SKILLS_CONTEXT_FAILED",
|
|
2911
|
+
"SKILLS_STORAGE_FULL",
|
|
2912
|
+
"PROFILE_LOCK_MISMATCH",
|
|
2913
|
+
"PROFILE_IDENTITY_MISMATCH",
|
|
2914
|
+
"PROFILE_APPLIED_REPORT_FAILED",
|
|
2915
|
+
"CACHED_AUTHORITY_REQUIRED",
|
|
2916
|
+
"CACHED_PROFILE_EXPIRED",
|
|
2917
|
+
"CACHED_PROFILE_MISSING",
|
|
2918
|
+
"CACHED_BUNDLE_MISSING",
|
|
2919
|
+
"INVALID_CONTEXT_INPUT",
|
|
2920
|
+
"CONTEXT_INPUT_TOO_LARGE",
|
|
2921
|
+
"INVALID_CONTEXT_BUDGET",
|
|
2922
|
+
"INVALID_SELECTION",
|
|
2923
|
+
"INVALID_SELECTION_ALIASES",
|
|
2924
|
+
"INVALID_RECEIPT",
|
|
2925
|
+
"INVALID_CACHE_FILE",
|
|
2926
|
+
"UNSAFE_CACHE_PATH",
|
|
2927
|
+
"RECEIPT_TOO_LARGE",
|
|
2928
|
+
"BUNDLE_DIGEST_MISMATCH",
|
|
2929
|
+
"BUNDLE_IDENTITY_MISMATCH",
|
|
2930
|
+
"BUNDLE_UNAVAILABLE",
|
|
2931
|
+
"BUNDLE_TOO_LARGE",
|
|
2932
|
+
"SKILL_NOT_SELECTED",
|
|
2933
|
+
"INVALID_SESSION",
|
|
2934
|
+
"SESSION_PARENT_NOT_FOUND",
|
|
2935
|
+
"SESSION_PARENT_MISMATCH",
|
|
2936
|
+
"SESSION_NOT_FOUND",
|
|
2937
|
+
"SESSION_GENERATION_CHANGED",
|
|
2938
|
+
"SESSION_GENERATION_EXHAUSTED",
|
|
2939
|
+
"SESSION_PARENT_CHANGED",
|
|
2940
|
+
"SESSION_RECEIPT_CHANGED",
|
|
2941
|
+
"SESSION_RECONCILIATION_REQUIRED",
|
|
2942
|
+
"SESSION_RENEWAL_TIMEOUT",
|
|
2943
|
+
"SESSION_CONTEXT_CONFLICT",
|
|
2944
|
+
"SESSION_RECONCILIATION_INCOMPLETE",
|
|
2945
|
+
"SESSION_WRITE_LOCKED",
|
|
2946
|
+
"SESSION_WRITE_LOCK_CHANGED",
|
|
2947
|
+
"SESSION_LOCK_RECOVERY_INCOMPLETE"
|
|
2948
|
+
]);
|
|
2949
|
+
var HOOK_REFUSAL_REASON_CODES = Object.freeze([
|
|
2950
|
+
...CONTEXT_ERROR_CODES,
|
|
2951
|
+
"SKILLS_HOOK_FAILED",
|
|
2952
|
+
"SKILLS_HOOK_TIMEOUT",
|
|
2953
|
+
"SKILLS_HOOK_INVALID_RESPONSE"
|
|
2954
|
+
]);
|
|
2955
|
+
var OPTIONAL_CONTEXT_FAILURES = new Set([
|
|
2956
|
+
"SKILLS_API_CREDENTIAL_UNAVAILABLE",
|
|
2957
|
+
"SKILLS_API_UNAVAILABLE",
|
|
2958
|
+
"SKILLS_API_UNAUTHORIZED",
|
|
2959
|
+
"SKILLS_API_FORBIDDEN",
|
|
2960
|
+
"SKILLS_API_RESOURCE_UNAVAILABLE",
|
|
2961
|
+
"CACHED_PROFILE_EXPIRED",
|
|
2962
|
+
"CACHED_PROFILE_MISSING",
|
|
2963
|
+
"CACHED_BUNDLE_MISSING",
|
|
2964
|
+
"SESSION_RECONCILIATION_REQUIRED",
|
|
2965
|
+
"SESSION_RENEWAL_TIMEOUT",
|
|
2966
|
+
"SESSION_WRITE_BUSY",
|
|
2967
|
+
"SESSION_PARENT_CHANGED",
|
|
2968
|
+
"SESSION_CONTEXT_CONFLICT",
|
|
2969
|
+
"SKILLS_HOOK_TIMEOUT"
|
|
2970
|
+
]);
|
|
2971
|
+
|
|
2972
|
+
// src/lib/agent-sumi.ts
|
|
2867
2973
|
var SUMI_PATH_SELECTORS = ["SUMI_HOME", "SUMI_CONFIG_DIR", "XDG_DATA_HOME", "XDG_CACHE_HOME", "XDG_CONFIG_HOME", "XDG_STATE_HOME"];
|
|
2868
2974
|
var SUMI_PATH_KEYS = ["data", "cache", "config", "state"];
|
|
2869
2975
|
var SUMI_PATH_OUTPUT_LIMIT = 128 * 1024;
|
|
@@ -3005,24 +3111,33 @@ function sumiConfigPath(home, cwd = process.cwd(), env = process.env) {
|
|
|
3005
3111
|
return join8(sumiConfigDirectory(home, env, cwd), "sumi.json");
|
|
3006
3112
|
}
|
|
3007
3113
|
function renderSumiPlugin(command, profile) {
|
|
3008
|
-
return renderSumiPluginVersion(command, profile,
|
|
3114
|
+
return renderSumiPluginVersion(command, profile, 3);
|
|
3009
3115
|
}
|
|
3010
3116
|
function isManagedSumiPlugin(text2, command, profile) {
|
|
3011
|
-
return text2 === renderSumiPlugin(command, profile) || text2 === renderSumiPluginVersion(command, profile,
|
|
3117
|
+
return text2 === renderSumiPlugin(command, profile) || text2 === renderSumiPluginVersion(command, profile, 2) || text2 === renderSumiPluginVersion(command, profile, 1);
|
|
3012
3118
|
}
|
|
3013
|
-
function renderSumiPluginVersion(command, profile,
|
|
3119
|
+
function renderSumiPluginVersion(command, profile, version2) {
|
|
3120
|
+
const legacy = version2 === 1;
|
|
3014
3121
|
const failure = (message) => legacy ? `new Error(${JSON.stringify(message)})` : "refusal()";
|
|
3015
3122
|
return `// Managed by @hasna/skills. Regenerate with skills hook install --agent sumi.
|
|
3016
3123
|
import { spawn } from "node:child_process";
|
|
3017
3124
|
const command = ${JSON.stringify(command)};
|
|
3018
3125
|
const profile = ${JSON.stringify(profile)};
|
|
3019
|
-
${legacy ? "" : `function refusal() {
|
|
3020
|
-
const error = new Error("Skills verification blocked this request. Review the Sumi Skills hook configuration, then retry.");
|
|
3126
|
+
${legacy ? "" : `function refusal(${version2 === 3 ? "reasonCode" : ""}) {
|
|
3127
|
+
const error = new Error("Skills verification blocked this request. Review the Sumi Skills hook configuration, then retry."${version2 === 3 ? ' + (reasonCode ? " [" + reasonCode + "]" : "")' : ""});
|
|
3021
3128
|
error.name = "SkillsHookRefusal";
|
|
3022
|
-
error.skillsHookRefusal = { version: 1, code: "SKILLS_HOOK_REFUSED" };
|
|
3129
|
+
error.skillsHookRefusal = { version: 1, code: "SKILLS_HOOK_REFUSED"${version2 === 3 ? ", ...(reasonCode ? { reasonCode } : {})" : ""} };
|
|
3023
3130
|
return error;
|
|
3024
3131
|
}
|
|
3025
|
-
`}
|
|
3132
|
+
`}${version2 === 3 ? `const reasonCodes = new Set(${JSON.stringify(HOOK_REFUSAL_REASON_CODES)});
|
|
3133
|
+
function refusedResult(result) {
|
|
3134
|
+
const reason = result.reason ?? result.stopReason ?? result.systemMessage;
|
|
3135
|
+
if (typeof reason !== "string" || reason.length > 16384) return refusal();
|
|
3136
|
+
if (reason.startsWith("NATIVE_SKILL_DRIFT:")) return refusal("NATIVE_SKILL_DRIFT");
|
|
3137
|
+
const code = /^Skills context is unavailable \\[([A-Z_]+)\\]/.exec(reason)?.[1];
|
|
3138
|
+
return refusal(reasonCodes.has(code) ? code : undefined);
|
|
3139
|
+
}
|
|
3140
|
+
` : ""}function context(input) {
|
|
3026
3141
|
return new Promise((resolve, reject) => {
|
|
3027
3142
|
const child = spawn(command, ["hook", "user-prompt", "--agent", "sumi", "--selection-profile", profile], { cwd: input.cwd, stdio: ["pipe", "pipe", "pipe"] });
|
|
3028
3143
|
const chunks = []; let bytes = 0, failure;
|
|
@@ -3037,12 +3152,12 @@ ${legacy ? "" : `function refusal() {
|
|
|
3037
3152
|
try {
|
|
3038
3153
|
const result = JSON.parse(Buffer.concat(chunks).toString("utf8"));
|
|
3039
3154
|
if (!result || typeof result !== "object" || Array.isArray(result)) throw ${failure("Invalid Skills hook response")};
|
|
3040
|
-
if (result.decision === "block" || result.continue === false) throw ${failure("Skills prompt hook refused")};
|
|
3155
|
+
if (result.decision === "block" || result.continue === false) throw ${version2 === 3 ? "refusedResult(result)" : failure("Skills prompt hook refused")};
|
|
3041
3156
|
if (Object.keys(result).length && (!result.hookSpecificOutput || result.hookSpecificOutput.hookEventName !== input.hook_event_name)) throw ${failure("Invalid Skills hook response")};
|
|
3042
3157
|
const text = result.hookSpecificOutput?.additionalContext ?? "";
|
|
3043
3158
|
if (typeof text !== "string") throw ${failure("Invalid Skills prompt context")};
|
|
3044
3159
|
resolve(text);
|
|
3045
|
-
} catch (error) { reject(${legacy ? "error" : "refusal()"}); }
|
|
3160
|
+
} catch (error) { reject(${legacy ? "error" : version2 === 3 ? 'error instanceof Error && error.name === "SkillsHookRefusal" ? error : refusal()' : "refusal()"}); }
|
|
3046
3161
|
});
|
|
3047
3162
|
child.stdin.end(JSON.stringify(input));
|
|
3048
3163
|
});
|
|
@@ -3075,9 +3190,13 @@ export default {
|
|
|
3075
3190
|
sessions.add(event.sessionID);
|
|
3076
3191
|
}));
|
|
3077
3192
|
registrations.push(await ctx.session.hook("context", async event => {
|
|
3078
|
-
|
|
3193
|
+
${version2 === 3 ? ` // Model messages also contain checkpoints, attachments and synthetic
|
|
3194
|
+
// user-role history, with no owner-prompt provenance. Prompt admission
|
|
3195
|
+
// already matched and persisted Skills; restore that verified state only.
|
|
3196
|
+
const prompt = "";
|
|
3197
|
+
` : ` const last = event.messages.findLast(message => message.role === "user");
|
|
3079
3198
|
const prompt = (last?.content ?? []).filter(part => part.type === "text").map(part => part.text).join("\\n");
|
|
3080
|
-
// Recheck on every primary request, including resumed and synthetic
|
|
3199
|
+
`} // Recheck on every primary request, including resumed and synthetic
|
|
3081
3200
|
// sessions. The CLI owns optional failures versus explicit refusals.
|
|
3082
3201
|
const text = await context(await input(event.sessionID, prompt, !sessions.has(event.sessionID)));
|
|
3083
3202
|
if (text) event.system.push({ type: "text", text });
|