@hasna/skills 0.10.63 → 0.10.65
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 +32 -0
- package/bin/index.js +2732 -1877
- package/bin/mcp.js +1472 -586
- package/bin/migrate.js +1 -1
- package/bin/server.js +1 -1
- package/bin/worker.js +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1798 -896
- package/dist/lib/agent-discovery.d.ts +26 -2
- package/dist/lib/agent-integration.d.ts +14 -1
- package/dist/lib/codex-plugin-skill-controls.d.ts +9 -0
- package/dist/lib/codex-semantic-cache-witness.d.ts +57 -0
- package/dist/lib/codex-settings-witness.d.ts +14 -0
- package/dist/lib/hook-diagnostics.d.ts +2 -0
- package/dist/runtime-prerequisites.js +146 -14
- package/dist/sdk/index.js +2093 -1193
- package/package.json +1 -1
|
@@ -4,19 +4,34 @@ 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
8
|
export { captureDiscoveryDirectories, type DiscoveryDirectory } from "./agent-discovery-directories.js";
|
|
8
9
|
export interface DiscoverySource {
|
|
9
10
|
reviewArtifact?: RetainedReviewArtifact;
|
|
10
11
|
path: string;
|
|
11
12
|
sha256: string | null;
|
|
12
|
-
hashMode?: "bytes" | "path-bytes" | "claude-plugin-manifest-v1" | "claude-plugin-registry" | "claude-marketplace-registry" | "claude-settings-v1" | "claude-settings-v2" | "claude-settings-v3" | "claude-settings-v4" | "claude-marketplace-registry-v2" | "codex-settings-v1" | "codex-settings-v2" | "codex-settings-v3" | "codex-settings-v4" | "sumi-settings-v1" | "claude-marketplace-entry-v1";
|
|
13
|
+
hashMode?: "bytes" | "path-bytes" | "claude-plugin-manifest-v1" | "claude-plugin-registry" | "claude-marketplace-registry" | "claude-settings-v1" | "claude-settings-v2" | "claude-settings-v3" | "claude-settings-v4" | "claude-marketplace-registry-v2" | "codex-settings-v1" | "codex-settings-v2" | "codex-settings-v3" | "codex-settings-v4" | "codex-settings-v5" | "sumi-settings-v1" | "claude-marketplace-entry-v1";
|
|
13
14
|
managedPlugins?: ManagedPluginRegistrationWitness[];
|
|
14
15
|
format?: "json" | "toml" | "yaml";
|
|
15
16
|
fields?: string[];
|
|
16
17
|
marketplace?: string;
|
|
17
18
|
plugin?: string;
|
|
18
19
|
}
|
|
20
|
+
export interface CodexSemanticCacheDiscovery {
|
|
21
|
+
schema: "hasna.codex-semantic-cache-discovery.v1";
|
|
22
|
+
configPath: string;
|
|
23
|
+
hookRootAlias: {
|
|
24
|
+
alias: string;
|
|
25
|
+
target: string;
|
|
26
|
+
} | null;
|
|
27
|
+
witness: CodexSemanticCacheWitness;
|
|
28
|
+
appOnlyParents: CodexSemanticCacheAppOnlyParent[];
|
|
29
|
+
previousPolicySha256: string;
|
|
30
|
+
supersededSources: DiscoverySource[];
|
|
31
|
+
supersededDirectories: DiscoveryDirectory[];
|
|
32
|
+
}
|
|
19
33
|
export interface AgentDiscoveryBinding {
|
|
34
|
+
codexSemanticCache?: CodexSemanticCacheDiscovery;
|
|
20
35
|
agent: IntegrationAgent;
|
|
21
36
|
roots: string[];
|
|
22
37
|
sources: DiscoverySource[];
|
|
@@ -76,7 +91,16 @@ export declare function projectNativeDiscoveryFields(text: string, format: "json
|
|
|
76
91
|
/** Called only after the owning planner validated discovery and capabilities.
|
|
77
92
|
* New roots must already be body-free; absent roots need the prior typed proof. */
|
|
78
93
|
export declare function captureRetiredCodexDiscovery(binding: AgentDiscoveryBinding, controls: CodexPluginSkillControl[], rules: unknown): AgentDiscoveryBinding;
|
|
79
|
-
|
|
94
|
+
/** Only native capability locations covered by the complete typed cache census
|
|
95
|
+
* 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 codexSemanticCacheDirectoryRole(path: string, cacheRoot: string): boolean;
|
|
98
|
+
export declare function assertCodexSemanticCacheHookSafety(cacheRoot: string, parents: readonly string[], configPath: string, hookRootAlias?: CodexSemanticCacheDiscovery["hookRootAlias"]): void;
|
|
99
|
+
export declare function verifyAgentDiscovery(binding: AgentDiscoveryBinding, codexRecovery?: CodexHookDiscoveryRecovery, reviewedRootAliases?: readonly {
|
|
100
|
+
agent: IntegrationAgent;
|
|
101
|
+
alias: string;
|
|
102
|
+
target: string;
|
|
103
|
+
}[]): void;
|
|
80
104
|
export declare function rebindAgentDiscovery(binding: AgentDiscoveryBinding, changes: Map<string, string>): AgentDiscoveryBinding;
|
|
81
105
|
/** Capture full byte witnesses for reviewed source or executable files; never import or execute them. */
|
|
82
106
|
export declare function captureDiscoveryByteSources(paths: string[]): DiscoverySource[];
|
|
@@ -48,6 +48,7 @@ export interface AgentConfigChange {
|
|
|
48
48
|
after: string;
|
|
49
49
|
}
|
|
50
50
|
export interface AgentIntegrationPlan {
|
|
51
|
+
cacheWitnessUpgrade?: CodexSemanticCacheUpgrade;
|
|
51
52
|
reviewArtifactSources?: Array<{
|
|
52
53
|
path: string;
|
|
53
54
|
sha256: string;
|
|
@@ -202,7 +203,7 @@ export declare function planClaudeHookEventsUpdate(options: ClaudeHookUpdateOpti
|
|
|
202
203
|
*/
|
|
203
204
|
export declare function planAgentSettingsWitnessUpgrade(options: {
|
|
204
205
|
agent: "claude" | "codex" | "sumi";
|
|
205
|
-
targetCodexVersion?: 2 | 3 | 4;
|
|
206
|
+
targetCodexVersion?: 2 | 3 | 4 | 5;
|
|
206
207
|
targetClaudeVersion?: 3 | 4;
|
|
207
208
|
reviewedPreimage: string;
|
|
208
209
|
home?: string;
|
|
@@ -211,6 +212,18 @@ export declare function planAgentSettingsWitnessUpgrade(options: {
|
|
|
211
212
|
expectedPolicySha256: string;
|
|
212
213
|
expectedSettingsSha256: string;
|
|
213
214
|
}): AgentIntegrationPlan;
|
|
215
|
+
export interface CodexSemanticCacheUpgrade {
|
|
216
|
+
agent: "codex";
|
|
217
|
+
appOnlyParents: string[];
|
|
218
|
+
expectedPolicySha256: string;
|
|
219
|
+
expectedSettingsSha256: string;
|
|
220
|
+
expectedCacheWitnessSha256?: string;
|
|
221
|
+
home?: string;
|
|
222
|
+
dataDir?: string;
|
|
223
|
+
}
|
|
224
|
+
/** Explicit current capability review. Old aggregate directory digests remain
|
|
225
|
+
* evidence, not a reconstructed historical tree or a new byte baseline. */
|
|
226
|
+
export declare function planCodexSemanticCacheWitnessUpgrade(options: CodexSemanticCacheUpgrade): AgentIntegrationPlan;
|
|
214
227
|
export interface ReviewArtifactMigration {
|
|
215
228
|
kind: "codex-native-catalog";
|
|
216
229
|
sourcePath: string;
|
|
@@ -32,6 +32,15 @@ export interface CodexPluginSourceInput {
|
|
|
32
32
|
sourceSha256: string;
|
|
33
33
|
}
|
|
34
34
|
type Read = (path: string) => string;
|
|
35
|
+
export interface CodexPluginRootControls {
|
|
36
|
+
namespace: string;
|
|
37
|
+
manifestSha256: string;
|
|
38
|
+
appSha256?: string;
|
|
39
|
+
mcpSha256?: string;
|
|
40
|
+
}
|
|
41
|
+
/** Reuse the strict package-owned capability parser for an explicitly reviewed
|
|
42
|
+
* plugin materialization. This does not establish parent or receipt identity. */
|
|
43
|
+
export declare function readCodexPluginRootControls(root: string, read: Read): CodexPluginRootControls;
|
|
35
44
|
export interface CodexPluginCacheDocumentIdentity {
|
|
36
45
|
name: string;
|
|
37
46
|
namespace: string;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { type CodexPluginSkillControl } from "./codex-plugin-skill-controls.js";
|
|
2
|
+
export declare const CODEX_SEMANTIC_CACHE_WITNESS_SCHEMA: "hasna.codex-semantic-cache-witness.v1";
|
|
3
|
+
export interface CodexSemanticCacheControl {
|
|
4
|
+
pluginId: string;
|
|
5
|
+
namespace: string;
|
|
6
|
+
pluginParent: string;
|
|
7
|
+
remotePluginId?: string;
|
|
8
|
+
receiptSha256?: string;
|
|
9
|
+
manifestSha256: string;
|
|
10
|
+
appSha256?: string;
|
|
11
|
+
mcpSha256?: string;
|
|
12
|
+
}
|
|
13
|
+
export interface CodexSemanticCacheSkill {
|
|
14
|
+
name: string;
|
|
15
|
+
pluginId: string;
|
|
16
|
+
namespace: string;
|
|
17
|
+
pluginParent: string;
|
|
18
|
+
disabledRules: string[];
|
|
19
|
+
}
|
|
20
|
+
/** Caller explicitly classifies a remote plugin parent whose only reviewed
|
|
21
|
+
* capability is its app integration and whose cache contains no Skill docs. */
|
|
22
|
+
export interface CodexSemanticCacheAppOnlyParent {
|
|
23
|
+
role: "app-only";
|
|
24
|
+
pluginId: string;
|
|
25
|
+
namespace: string;
|
|
26
|
+
pluginParent: string;
|
|
27
|
+
remotePluginId: string;
|
|
28
|
+
}
|
|
29
|
+
export interface CodexSemanticCacheAppOnlyParentWitness extends CodexSemanticCacheAppOnlyParent {
|
|
30
|
+
receiptSha256: string;
|
|
31
|
+
manifestSha256: string;
|
|
32
|
+
appSha256: string;
|
|
33
|
+
mcpSha256?: string;
|
|
34
|
+
}
|
|
35
|
+
export interface CodexSemanticCacheWitness {
|
|
36
|
+
schema: typeof CODEX_SEMANTIC_CACHE_WITNESS_SCHEMA;
|
|
37
|
+
cacheRoot: string;
|
|
38
|
+
controls: CodexSemanticCacheControl[];
|
|
39
|
+
appOnlyParents: CodexSemanticCacheAppOnlyParentWitness[];
|
|
40
|
+
skills: CodexSemanticCacheSkill[];
|
|
41
|
+
sha256: string;
|
|
42
|
+
}
|
|
43
|
+
export interface CaptureCodexSemanticCacheWitnessOptions {
|
|
44
|
+
cacheRoot: string;
|
|
45
|
+
controls: CodexPluginSkillControl[];
|
|
46
|
+
/** Explicit package-reviewed parents; absence never infers this role. */
|
|
47
|
+
appOnlyParents?: CodexSemanticCacheAppOnlyParent[];
|
|
48
|
+
rules: unknown;
|
|
49
|
+
/** Package-owned, bounded read used by the existing semantic control review. */
|
|
50
|
+
read?: (path: string) => string;
|
|
51
|
+
}
|
|
52
|
+
/** Capture the current semantic identity without changing the stored baseline.
|
|
53
|
+
* Existing package-owned parsers validate documents and capability controls. */
|
|
54
|
+
export declare function captureCodexSemanticCacheWitness(options: CaptureCodexSemanticCacheWitnessOptions): CodexSemanticCacheWitness;
|
|
55
|
+
/** Verify without mutating or replacing the saved witness. Version refreshes
|
|
56
|
+
* pass only when their complete semantic projection and qualified names match. */
|
|
57
|
+
export declare function assertCodexSemanticCacheWitnessUnchanged(previous: CodexSemanticCacheWitness, current: CodexSemanticCacheWitness): void;
|
|
@@ -46,3 +46,17 @@ export declare function upgradeCodexSettingsWitnessV4(previous: {
|
|
|
46
46
|
hashMode?: "bytes" | "codex-settings-v1" | "codex-settings-v2" | "codex-settings-v3";
|
|
47
47
|
sha256: string;
|
|
48
48
|
}, reviewedSettingsPath: string): ReturnType<typeof captureCodexSettingsV4>;
|
|
49
|
+
/** Explicit V5 adds only schema-valid root status-line string vectors. */
|
|
50
|
+
export declare function hashCodexSettingsReplacementV5(text: string, budget?: ClaudeSettingsWitnessBudget): string;
|
|
51
|
+
export declare function captureCodexSettingsV5(path: string, budget?: ClaudeSettingsWitnessBudget): {
|
|
52
|
+
path: string;
|
|
53
|
+
hashMode: "codex-settings-v5";
|
|
54
|
+
sha256: string;
|
|
55
|
+
};
|
|
56
|
+
/** A V5 hash never verifies an old pin: prove the preserved original under its
|
|
57
|
+
* exact original contract before comparing old/current under V5. */
|
|
58
|
+
export declare function upgradeCodexSettingsWitnessV5(previous: {
|
|
59
|
+
path: string;
|
|
60
|
+
hashMode?: "bytes" | "codex-settings-v1" | "codex-settings-v2" | "codex-settings-v3" | "codex-settings-v4";
|
|
61
|
+
sha256: string;
|
|
62
|
+
}, reviewedSettingsPath: string): ReturnType<typeof captureCodexSettingsV5>;
|
|
@@ -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.65";
|
|
53
53
|
var package_default = {
|
|
54
54
|
name: "@hasna/skills",
|
|
55
55
|
version,
|
|
@@ -2642,6 +2642,53 @@ function assertAgentPolicyCollections(policy) {
|
|
|
2642
2642
|
requireBound(directory.sha256 === null || typeof directory.sha256 === "string" && /^[a-f0-9]{64}$/.test(directory.sha256));
|
|
2643
2643
|
}
|
|
2644
2644
|
}
|
|
2645
|
+
if (value.codexSemanticCache !== undefined) {
|
|
2646
|
+
const proof = record(value.codexSemanticCache, 8), witness = record(proof.witness, 6);
|
|
2647
|
+
requireBound(agent === "codex" && value.agent === "codex" && value.method === "reviewed" && Object.keys(proof).length === 8 && proof.schema === "hasna.codex-semantic-cache-discovery.v1" && typeof proof.previousPolicySha256 === "string" && /^[a-f0-9]{64}$/.test(proof.previousPolicySha256) && Object.keys(witness).length === 6 && witness.schema === "hasna.codex-semantic-cache-witness.v1" && typeof witness.sha256 === "string" && /^[a-f0-9]{64}$/.test(witness.sha256));
|
|
2648
|
+
text(proof.configPath);
|
|
2649
|
+
requireBound(isAbsolute3(proof.configPath) && resolve5(proof.configPath) === proof.configPath && basename3(proof.configPath) === "config.toml");
|
|
2650
|
+
text(witness.cacheRoot);
|
|
2651
|
+
requireBound(isAbsolute3(witness.cacheRoot) && resolve5(witness.cacheRoot) === witness.cacheRoot);
|
|
2652
|
+
if (proof.hookRootAlias !== null) {
|
|
2653
|
+
requireBound(object(proof.hookRootAlias) && Object.keys(proof.hookRootAlias).length === 2);
|
|
2654
|
+
for (const key of ["alias", "target"]) {
|
|
2655
|
+
text(proof.hookRootAlias[key]);
|
|
2656
|
+
requireBound(isAbsolute3(proof.hookRootAlias[key]) && resolve5(proof.hookRootAlias[key]) === proof.hookRootAlias[key]);
|
|
2657
|
+
}
|
|
2658
|
+
}
|
|
2659
|
+
const parents2 = array(proof.appOnlyParents, 4096);
|
|
2660
|
+
for (const parent of parents2) {
|
|
2661
|
+
requireBound(object(parent) && Object.keys(parent).length === 5 && parent.role === "app-only");
|
|
2662
|
+
for (const key of ["pluginId", "namespace", "pluginParent", "remotePluginId"])
|
|
2663
|
+
text(parent[key]);
|
|
2664
|
+
requireBound(isAbsolute3(parent.pluginParent) && resolve5(parent.pluginParent) === parent.pluginParent);
|
|
2665
|
+
}
|
|
2666
|
+
for (const row of [...array(witness.controls, 4096), ...array(witness.appOnlyParents, 4096)]) {
|
|
2667
|
+
requireBound(object(row));
|
|
2668
|
+
for (const key of ["pluginId", "namespace", "pluginParent"])
|
|
2669
|
+
text(row[key]);
|
|
2670
|
+
for (const key of ["manifestSha256", "appSha256", "mcpSha256", "receiptSha256"])
|
|
2671
|
+
if (row[key] !== undefined)
|
|
2672
|
+
requireBound(typeof row[key] === "string" && /^[a-f0-9]{64}$/.test(row[key]));
|
|
2673
|
+
}
|
|
2674
|
+
for (const skill of array(witness.skills, 4096)) {
|
|
2675
|
+
requireBound(object(skill));
|
|
2676
|
+
for (const key of ["name", "pluginId", "namespace", "pluginParent"])
|
|
2677
|
+
text(skill[key]);
|
|
2678
|
+
for (const rule of array(skill.disabledRules, 4096))
|
|
2679
|
+
text(rule);
|
|
2680
|
+
}
|
|
2681
|
+
for (const source of array(proof.supersededSources, AGENT_POLICY_LIMITS.discoverySources)) {
|
|
2682
|
+
requireBound(object(source));
|
|
2683
|
+
text(source.path);
|
|
2684
|
+
requireBound(source.sha256 === null || typeof source.sha256 === "string" && /^[a-f0-9]{64}$/.test(source.sha256));
|
|
2685
|
+
}
|
|
2686
|
+
for (const directory of array(proof.supersededDirectories, AGENT_POLICY_LIMITS.discoveryDirectories)) {
|
|
2687
|
+
requireBound(object(directory));
|
|
2688
|
+
text(directory.path);
|
|
2689
|
+
requireBound(directory.sha256 === null || typeof directory.sha256 === "string" && /^[a-f0-9]{64}$/.test(directory.sha256));
|
|
2690
|
+
}
|
|
2691
|
+
}
|
|
2645
2692
|
for (const root of array(value.roots, AGENT_POLICY_LIMITS.discoveryRoots))
|
|
2646
2693
|
text(root);
|
|
2647
2694
|
for (const source of array(value.sources, AGENT_POLICY_LIMITS.discoverySources)) {
|
|
@@ -2649,12 +2696,12 @@ function assertAgentPolicyCollections(policy) {
|
|
|
2649
2696
|
text(source.path);
|
|
2650
2697
|
requireBound(source.sha256 === null || typeof source.sha256 === "string" && /^[a-f0-9]{64}$/.test(source.sha256));
|
|
2651
2698
|
if (source.hashMode !== undefined)
|
|
2652
|
-
requireBound(["bytes", "path-bytes", "claude-plugin-manifest-v1", "claude-plugin-registry", "claude-marketplace-registry", "claude-settings-v1", "claude-settings-v2", "claude-settings-v3", "claude-settings-v4", "claude-marketplace-registry-v2", "codex-settings-v1", "codex-settings-v2", "codex-settings-v3", "codex-settings-v4", "sumi-settings-v1", "claude-marketplace-entry-v1"].includes(source.hashMode) && source.format === undefined && source.fields === undefined && (source.hashMode === "bytes" || source.sha256 !== null));
|
|
2699
|
+
requireBound(["bytes", "path-bytes", "claude-plugin-manifest-v1", "claude-plugin-registry", "claude-marketplace-registry", "claude-settings-v1", "claude-settings-v2", "claude-settings-v3", "claude-settings-v4", "claude-marketplace-registry-v2", "codex-settings-v1", "codex-settings-v2", "codex-settings-v3", "codex-settings-v4", "codex-settings-v5", "sumi-settings-v1", "claude-marketplace-entry-v1"].includes(source.hashMode) && source.format === undefined && source.fields === undefined && (source.hashMode === "bytes" || source.sha256 !== null));
|
|
2653
2700
|
if (source.hashMode === "claude-plugin-manifest-v1")
|
|
2654
2701
|
requireBound(agent === "claude" && value.agent === "claude" && ["automatic", "reviewed"].includes(value.method) && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute3(source.path) && resolve5(source.path) === source.path && source.path.endsWith(join5(sep, ".claude-plugin", "plugin.json")) && source.managedPlugins === undefined);
|
|
2655
2702
|
if (source.hashMode === "sumi-settings-v1")
|
|
2656
2703
|
requireBound(agent === "sumi" && value.agent === "sumi" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute3(source.path) && resolve5(source.path) === source.path && basename3(source.path) === "sumi.json");
|
|
2657
|
-
if (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4"))
|
|
2704
|
+
if (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4" || source.hashMode === "codex-settings-v5"))
|
|
2658
2705
|
requireBound(agent === "codex" && value.agent === "codex" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute3(source.path) && resolve5(source.path) === source.path && basename3(source.path) === "config.toml");
|
|
2659
2706
|
if (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || source.hashMode === "claude-settings-v4"))
|
|
2660
2707
|
requireBound(agent === "claude" && value.agent === "claude" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute3(source.path) && resolve5(source.path) === source.path && basename3(source.path) === "settings.json");
|
|
@@ -2817,6 +2864,78 @@ function detectedIntegrationAgents(options = {}) {
|
|
|
2817
2864
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
2818
2865
|
import { accessSync, constants as constants4, existsSync as existsSync4, lstatSync as lstatSync5, realpathSync, statSync as statSync2 } from "fs";
|
|
2819
2866
|
import { delimiter, isAbsolute as isAbsolute4, join as join8, resolve as resolve7 } from "path";
|
|
2867
|
+
|
|
2868
|
+
// src/lib/hook-diagnostics.ts
|
|
2869
|
+
var CONTEXT_ERROR_CODES = new Set([
|
|
2870
|
+
"SKILLS_API_CREDENTIAL_UNAVAILABLE",
|
|
2871
|
+
"SKILLS_API_UNAVAILABLE",
|
|
2872
|
+
"SKILLS_API_UNAUTHORIZED",
|
|
2873
|
+
"SKILLS_API_FORBIDDEN",
|
|
2874
|
+
"SKILLS_API_RESOURCE_UNAVAILABLE",
|
|
2875
|
+
"SESSION_WRITE_BUSY",
|
|
2876
|
+
"SKILLS_CONTEXT_FAILED",
|
|
2877
|
+
"SKILLS_STORAGE_FULL",
|
|
2878
|
+
"PROFILE_LOCK_MISMATCH",
|
|
2879
|
+
"PROFILE_IDENTITY_MISMATCH",
|
|
2880
|
+
"PROFILE_APPLIED_REPORT_FAILED",
|
|
2881
|
+
"CACHED_AUTHORITY_REQUIRED",
|
|
2882
|
+
"CACHED_PROFILE_EXPIRED",
|
|
2883
|
+
"CACHED_PROFILE_MISSING",
|
|
2884
|
+
"CACHED_BUNDLE_MISSING",
|
|
2885
|
+
"INVALID_CONTEXT_INPUT",
|
|
2886
|
+
"CONTEXT_INPUT_TOO_LARGE",
|
|
2887
|
+
"INVALID_CONTEXT_BUDGET",
|
|
2888
|
+
"INVALID_SELECTION",
|
|
2889
|
+
"INVALID_SELECTION_ALIASES",
|
|
2890
|
+
"INVALID_RECEIPT",
|
|
2891
|
+
"INVALID_CACHE_FILE",
|
|
2892
|
+
"UNSAFE_CACHE_PATH",
|
|
2893
|
+
"RECEIPT_TOO_LARGE",
|
|
2894
|
+
"BUNDLE_DIGEST_MISMATCH",
|
|
2895
|
+
"BUNDLE_IDENTITY_MISMATCH",
|
|
2896
|
+
"BUNDLE_UNAVAILABLE",
|
|
2897
|
+
"BUNDLE_TOO_LARGE",
|
|
2898
|
+
"SKILL_NOT_SELECTED",
|
|
2899
|
+
"INVALID_SESSION",
|
|
2900
|
+
"SESSION_PARENT_NOT_FOUND",
|
|
2901
|
+
"SESSION_PARENT_MISMATCH",
|
|
2902
|
+
"SESSION_NOT_FOUND",
|
|
2903
|
+
"SESSION_GENERATION_CHANGED",
|
|
2904
|
+
"SESSION_GENERATION_EXHAUSTED",
|
|
2905
|
+
"SESSION_PARENT_CHANGED",
|
|
2906
|
+
"SESSION_RECEIPT_CHANGED",
|
|
2907
|
+
"SESSION_RECONCILIATION_REQUIRED",
|
|
2908
|
+
"SESSION_RENEWAL_TIMEOUT",
|
|
2909
|
+
"SESSION_CONTEXT_CONFLICT",
|
|
2910
|
+
"SESSION_RECONCILIATION_INCOMPLETE",
|
|
2911
|
+
"SESSION_WRITE_LOCKED",
|
|
2912
|
+
"SESSION_WRITE_LOCK_CHANGED",
|
|
2913
|
+
"SESSION_LOCK_RECOVERY_INCOMPLETE"
|
|
2914
|
+
]);
|
|
2915
|
+
var HOOK_REFUSAL_REASON_CODES = Object.freeze([
|
|
2916
|
+
...CONTEXT_ERROR_CODES,
|
|
2917
|
+
"SKILLS_HOOK_FAILED",
|
|
2918
|
+
"SKILLS_HOOK_TIMEOUT",
|
|
2919
|
+
"SKILLS_HOOK_INVALID_RESPONSE"
|
|
2920
|
+
]);
|
|
2921
|
+
var OPTIONAL_CONTEXT_FAILURES = new Set([
|
|
2922
|
+
"SKILLS_API_CREDENTIAL_UNAVAILABLE",
|
|
2923
|
+
"SKILLS_API_UNAVAILABLE",
|
|
2924
|
+
"SKILLS_API_UNAUTHORIZED",
|
|
2925
|
+
"SKILLS_API_FORBIDDEN",
|
|
2926
|
+
"SKILLS_API_RESOURCE_UNAVAILABLE",
|
|
2927
|
+
"CACHED_PROFILE_EXPIRED",
|
|
2928
|
+
"CACHED_PROFILE_MISSING",
|
|
2929
|
+
"CACHED_BUNDLE_MISSING",
|
|
2930
|
+
"SESSION_RECONCILIATION_REQUIRED",
|
|
2931
|
+
"SESSION_RENEWAL_TIMEOUT",
|
|
2932
|
+
"SESSION_WRITE_BUSY",
|
|
2933
|
+
"SESSION_PARENT_CHANGED",
|
|
2934
|
+
"SESSION_CONTEXT_CONFLICT",
|
|
2935
|
+
"SKILLS_HOOK_TIMEOUT"
|
|
2936
|
+
]);
|
|
2937
|
+
|
|
2938
|
+
// src/lib/agent-sumi.ts
|
|
2820
2939
|
var SUMI_PATH_SELECTORS = ["SUMI_HOME", "SUMI_CONFIG_DIR", "XDG_DATA_HOME", "XDG_CACHE_HOME", "XDG_CONFIG_HOME", "XDG_STATE_HOME"];
|
|
2821
2940
|
var SUMI_PATH_KEYS = ["data", "cache", "config", "state"];
|
|
2822
2941
|
var SUMI_PATH_OUTPUT_LIMIT = 128 * 1024;
|
|
@@ -2958,24 +3077,33 @@ function sumiConfigPath(home, cwd = process.cwd(), env = process.env) {
|
|
|
2958
3077
|
return join8(sumiConfigDirectory(home, env, cwd), "sumi.json");
|
|
2959
3078
|
}
|
|
2960
3079
|
function renderSumiPlugin(command, profile) {
|
|
2961
|
-
return renderSumiPluginVersion(command, profile,
|
|
3080
|
+
return renderSumiPluginVersion(command, profile, 3);
|
|
2962
3081
|
}
|
|
2963
3082
|
function isManagedSumiPlugin(text2, command, profile) {
|
|
2964
|
-
return text2 === renderSumiPlugin(command, profile) || text2 === renderSumiPluginVersion(command, profile,
|
|
3083
|
+
return text2 === renderSumiPlugin(command, profile) || text2 === renderSumiPluginVersion(command, profile, 2) || text2 === renderSumiPluginVersion(command, profile, 1);
|
|
2965
3084
|
}
|
|
2966
|
-
function renderSumiPluginVersion(command, profile,
|
|
3085
|
+
function renderSumiPluginVersion(command, profile, version2) {
|
|
3086
|
+
const legacy = version2 === 1;
|
|
2967
3087
|
const failure = (message) => legacy ? `new Error(${JSON.stringify(message)})` : "refusal()";
|
|
2968
3088
|
return `// Managed by @hasna/skills. Regenerate with skills hook install --agent sumi.
|
|
2969
3089
|
import { spawn } from "node:child_process";
|
|
2970
3090
|
const command = ${JSON.stringify(command)};
|
|
2971
3091
|
const profile = ${JSON.stringify(profile)};
|
|
2972
|
-
${legacy ? "" : `function refusal() {
|
|
2973
|
-
const error = new Error("Skills verification blocked this request. Review the Sumi Skills hook configuration, then retry.");
|
|
3092
|
+
${legacy ? "" : `function refusal(${version2 === 3 ? "reasonCode" : ""}) {
|
|
3093
|
+
const error = new Error("Skills verification blocked this request. Review the Sumi Skills hook configuration, then retry."${version2 === 3 ? ' + (reasonCode ? " [" + reasonCode + "]" : "")' : ""});
|
|
2974
3094
|
error.name = "SkillsHookRefusal";
|
|
2975
|
-
error.skillsHookRefusal = { version: 1, code: "SKILLS_HOOK_REFUSED" };
|
|
3095
|
+
error.skillsHookRefusal = { version: 1, code: "SKILLS_HOOK_REFUSED"${version2 === 3 ? ", ...(reasonCode ? { reasonCode } : {})" : ""} };
|
|
2976
3096
|
return error;
|
|
2977
3097
|
}
|
|
2978
|
-
`}
|
|
3098
|
+
`}${version2 === 3 ? `const reasonCodes = new Set(${JSON.stringify(HOOK_REFUSAL_REASON_CODES)});
|
|
3099
|
+
function refusedResult(result) {
|
|
3100
|
+
const reason = result.reason ?? result.stopReason ?? result.systemMessage;
|
|
3101
|
+
if (typeof reason !== "string" || reason.length > 16384) return refusal();
|
|
3102
|
+
if (reason.startsWith("NATIVE_SKILL_DRIFT:")) return refusal("NATIVE_SKILL_DRIFT");
|
|
3103
|
+
const code = /^Skills context is unavailable \\[([A-Z_]+)\\]/.exec(reason)?.[1];
|
|
3104
|
+
return refusal(reasonCodes.has(code) ? code : undefined);
|
|
3105
|
+
}
|
|
3106
|
+
` : ""}function context(input) {
|
|
2979
3107
|
return new Promise((resolve, reject) => {
|
|
2980
3108
|
const child = spawn(command, ["hook", "user-prompt", "--agent", "sumi", "--selection-profile", profile], { cwd: input.cwd, stdio: ["pipe", "pipe", "pipe"] });
|
|
2981
3109
|
const chunks = []; let bytes = 0, failure;
|
|
@@ -2990,12 +3118,12 @@ ${legacy ? "" : `function refusal() {
|
|
|
2990
3118
|
try {
|
|
2991
3119
|
const result = JSON.parse(Buffer.concat(chunks).toString("utf8"));
|
|
2992
3120
|
if (!result || typeof result !== "object" || Array.isArray(result)) throw ${failure("Invalid Skills hook response")};
|
|
2993
|
-
if (result.decision === "block" || result.continue === false) throw ${failure("Skills prompt hook refused")};
|
|
3121
|
+
if (result.decision === "block" || result.continue === false) throw ${version2 === 3 ? "refusedResult(result)" : failure("Skills prompt hook refused")};
|
|
2994
3122
|
if (Object.keys(result).length && (!result.hookSpecificOutput || result.hookSpecificOutput.hookEventName !== input.hook_event_name)) throw ${failure("Invalid Skills hook response")};
|
|
2995
3123
|
const text = result.hookSpecificOutput?.additionalContext ?? "";
|
|
2996
3124
|
if (typeof text !== "string") throw ${failure("Invalid Skills prompt context")};
|
|
2997
3125
|
resolve(text);
|
|
2998
|
-
} catch (error) { reject(${legacy ? "error" : "refusal()"}); }
|
|
3126
|
+
} catch (error) { reject(${legacy ? "error" : version2 === 3 ? 'error instanceof Error && error.name === "SkillsHookRefusal" ? error : refusal()' : "refusal()"}); }
|
|
2999
3127
|
});
|
|
3000
3128
|
child.stdin.end(JSON.stringify(input));
|
|
3001
3129
|
});
|
|
@@ -3028,9 +3156,13 @@ export default {
|
|
|
3028
3156
|
sessions.add(event.sessionID);
|
|
3029
3157
|
}));
|
|
3030
3158
|
registrations.push(await ctx.session.hook("context", async event => {
|
|
3031
|
-
|
|
3159
|
+
${version2 === 3 ? ` // Model messages also contain checkpoints, attachments and synthetic
|
|
3160
|
+
// user-role history, with no owner-prompt provenance. Prompt admission
|
|
3161
|
+
// already matched and persisted Skills; restore that verified state only.
|
|
3162
|
+
const prompt = "";
|
|
3163
|
+
` : ` const last = event.messages.findLast(message => message.role === "user");
|
|
3032
3164
|
const prompt = (last?.content ?? []).filter(part => part.type === "text").map(part => part.text).join("\\n");
|
|
3033
|
-
// Recheck on every primary request, including resumed and synthetic
|
|
3165
|
+
`} // Recheck on every primary request, including resumed and synthetic
|
|
3034
3166
|
// sessions. The CLI owns optional failures versus explicit refusals.
|
|
3035
3167
|
const text = await context(await input(event.sessionID, prompt, !sessions.has(event.sessionID)));
|
|
3036
3168
|
if (text) event.system.push({ type: "text", text });
|