@hasna/skills 0.10.64 → 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 +6 -0
- package/bin/index.js +150 -130
- package/bin/mcp.js +75 -1
- package/bin/migrate.js +1 -1
- package/bin/server.js +1 -1
- package/bin/worker.js +1 -1
- package/dist/index.js +97 -12
- package/dist/lib/hook-diagnostics.d.ts +2 -0
- package/dist/runtime-prerequisites.js +97 -12
- package/dist/sdk/index.js +97 -12
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -334,6 +334,12 @@ Sumi uses its native V2 plugin API, independently of OpenCode. Its managed
|
|
|
334
334
|
`plugins/skills-cli.js` plugin validates prompt admission and adds selected
|
|
335
335
|
instructions to transient request context. Real native session IDs and parent
|
|
336
336
|
IDs bind exact inherited pins; native agent definitions are not Hasna identities.
|
|
337
|
+
Prompt matching uses the native prompt-admission event. Request context restores
|
|
338
|
+
verified loaded Skills and never treats model history, attachments or compaction
|
|
339
|
+
checkpoints as a new owner prompt. The complete native model context is unchanged;
|
|
340
|
+
the hook's input limits and native/integrity refusals remain strict. Adapter
|
|
341
|
+
refusals retain only fixed reason codes, never child diagnostic prose. Both older
|
|
342
|
+
managed plugin forms remain recognized for a reviewed hook-install upgrade.
|
|
337
343
|
Native payload tool calls and prompt attachments are refused except for the
|
|
338
344
|
`skills-cli` bridge. The supported configuration resolver honors `SUMI_CONFIG_DIR`,
|
|
339
345
|
`XDG_CONFIG_HOME` and `SUMI_HOME` without performing native home adoption.
|
package/bin/index.js
CHANGED
|
@@ -36888,7 +36888,7 @@ var package_default;
|
|
|
36888
36888
|
var init_package = __esm(() => {
|
|
36889
36889
|
package_default = {
|
|
36890
36890
|
name: "@hasna/skills",
|
|
36891
|
-
version: "0.10.
|
|
36891
|
+
version: "0.10.65",
|
|
36892
36892
|
description: "Skills library for AI coding agents",
|
|
36893
36893
|
type: "module",
|
|
36894
36894
|
bin: {
|
|
@@ -54750,6 +54750,130 @@ var init_agent_codex = __esm(() => {
|
|
|
54750
54750
|
CODEX_SKILL_CONFIG_SECTIONS = /^\[\[skills\.config\]\][^\n]*(?:\n(?!\s*\[)[^\n]*)*/gm;
|
|
54751
54751
|
});
|
|
54752
54752
|
|
|
54753
|
+
// src/lib/hook-diagnostics.ts
|
|
54754
|
+
function hookChildError(stdout, stage) {
|
|
54755
|
+
let code;
|
|
54756
|
+
if (stdout.length <= 16 * 1024) {
|
|
54757
|
+
try {
|
|
54758
|
+
code = JSON.parse(stdout)?.error?.code;
|
|
54759
|
+
} catch {}
|
|
54760
|
+
}
|
|
54761
|
+
return new HookDiagnosticError(typeof code === "string" && CONTEXT_ERROR_CODES.has(code) ? code : "SKILLS_CONTEXT_FAILED", stage);
|
|
54762
|
+
}
|
|
54763
|
+
function hookFailureReason(error, profileId) {
|
|
54764
|
+
const owned = error instanceof HookDiagnosticError;
|
|
54765
|
+
const code = owned && (CONTEXT_ERROR_CODES.has(error.code) || ["SKILLS_HOOK_TIMEOUT", "SKILLS_HOOK_INVALID_RESPONSE"].includes(error.code)) ? error.code : "SKILLS_HOOK_FAILED";
|
|
54766
|
+
const profile = typeof profileId === "string" && /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(profileId) && !profileId.includes("..") ? profileId : undefined;
|
|
54767
|
+
const identity6 = [profile ? `profile=${profile}` : undefined, owned ? `stage=${error.stage}` : undefined].filter(Boolean).join(", ");
|
|
54768
|
+
const prefix = `Skills context is unavailable [${code}]${identity6 ? ` (${identity6})` : ""}.`;
|
|
54769
|
+
if (code === "SKILLS_STORAGE_FULL") {
|
|
54770
|
+
return `${prefix} The filesystem is full or its quota is exhausted. Free space through your approved recoverable cleanup process, then retry the same session. Do not delete session receipts or change credentials to bypass this failure.`;
|
|
54771
|
+
}
|
|
54772
|
+
if (code === "PROFILE_LOCK_MISMATCH") {
|
|
54773
|
+
return `${prefix} This session or project is pinned to a different profile. Inspect the session with skills sessions show <session-id> --json; review skills sessions reconcile for an intentional session change, or explicit project sync for a project change. Sync alone does not change that pin.`;
|
|
54774
|
+
}
|
|
54775
|
+
if (code === "SESSION_RECONCILIATION_REQUIRED") {
|
|
54776
|
+
return `${prefix} A loaded skill changed or was removed. Inspect this session with skills sessions show <session-id> --json, then review skills sessions reconcile for an intentional change. Sync alone does not change this pin. A refusal is re-checked with the authority within five minutes, or sooner after the receipt or the synced profile revision changes.`;
|
|
54777
|
+
}
|
|
54778
|
+
if (code === "SESSION_RENEWAL_TIMEOUT") {
|
|
54779
|
+
return `${prefix} Renewing this session's expired authorization did not finish within the hook's renewal window; no request was refused. Sync and diagnosis do not change this window. The existing pin is unchanged and the next prompt retries the renewal. If it keeps recurring, inspect the session with skills sessions show <session-id> --json and review skills sessions reconcile.`;
|
|
54780
|
+
}
|
|
54781
|
+
if (code.startsWith("SESSION_")) {
|
|
54782
|
+
return `${prefix} Inspect the session with skills sessions show <session-id> --json and review its parent or concurrent writer before retrying. Do not remove or rewrite its receipt to bypass this refusal.`;
|
|
54783
|
+
}
|
|
54784
|
+
if (["PROFILE_IDENTITY_MISMATCH", "BUNDLE_DIGEST_MISMATCH", "BUNDLE_IDENTITY_MISMATCH", "INVALID_RECEIPT", "INVALID_CACHE_FILE", "UNSAFE_CACHE_PATH"].includes(code)) {
|
|
54785
|
+
return `${prefix} Verify the selected authority and receipt integrity before repairing the profile; do not bypass verification or substitute another cache.`;
|
|
54786
|
+
}
|
|
54787
|
+
const selected = profile ?? "<id>";
|
|
54788
|
+
return `${prefix} Run skills sync --selection-profile ${selected} --json, then diagnose with skills context --stdin --json --cached --selection-profile ${selected} using the same hook session and working-directory fields. Do not include credentials in diagnostic input.`;
|
|
54789
|
+
}
|
|
54790
|
+
function isOptionalHookContextFailure(error) {
|
|
54791
|
+
return error instanceof HookDiagnosticError && OPTIONAL_CONTEXT_FAILURES.has(error.code);
|
|
54792
|
+
}
|
|
54793
|
+
function hookUnavailableContext(error, profileId) {
|
|
54794
|
+
return `${hookFailureReason(error, profileId)} No Skills instructions were delivered by this hook. Ordinary work may continue. Do not perform actions that depend on unavailable Skills instructions, including previously loaded instructions. Explicit skills load, context and run still require verification. Do not substitute native, unmanaged or stale payloads.`;
|
|
54795
|
+
}
|
|
54796
|
+
var CONTEXT_ERROR_CODES, HOOK_REFUSAL_REASON_CODES, HookDiagnosticError, OPTIONAL_CONTEXT_FAILURES;
|
|
54797
|
+
var init_hook_diagnostics = __esm(() => {
|
|
54798
|
+
CONTEXT_ERROR_CODES = new Set([
|
|
54799
|
+
"SKILLS_API_CREDENTIAL_UNAVAILABLE",
|
|
54800
|
+
"SKILLS_API_UNAVAILABLE",
|
|
54801
|
+
"SKILLS_API_UNAUTHORIZED",
|
|
54802
|
+
"SKILLS_API_FORBIDDEN",
|
|
54803
|
+
"SKILLS_API_RESOURCE_UNAVAILABLE",
|
|
54804
|
+
"SESSION_WRITE_BUSY",
|
|
54805
|
+
"SKILLS_CONTEXT_FAILED",
|
|
54806
|
+
"SKILLS_STORAGE_FULL",
|
|
54807
|
+
"PROFILE_LOCK_MISMATCH",
|
|
54808
|
+
"PROFILE_IDENTITY_MISMATCH",
|
|
54809
|
+
"PROFILE_APPLIED_REPORT_FAILED",
|
|
54810
|
+
"CACHED_AUTHORITY_REQUIRED",
|
|
54811
|
+
"CACHED_PROFILE_EXPIRED",
|
|
54812
|
+
"CACHED_PROFILE_MISSING",
|
|
54813
|
+
"CACHED_BUNDLE_MISSING",
|
|
54814
|
+
"INVALID_CONTEXT_INPUT",
|
|
54815
|
+
"CONTEXT_INPUT_TOO_LARGE",
|
|
54816
|
+
"INVALID_CONTEXT_BUDGET",
|
|
54817
|
+
"INVALID_SELECTION",
|
|
54818
|
+
"INVALID_SELECTION_ALIASES",
|
|
54819
|
+
"INVALID_RECEIPT",
|
|
54820
|
+
"INVALID_CACHE_FILE",
|
|
54821
|
+
"UNSAFE_CACHE_PATH",
|
|
54822
|
+
"RECEIPT_TOO_LARGE",
|
|
54823
|
+
"BUNDLE_DIGEST_MISMATCH",
|
|
54824
|
+
"BUNDLE_IDENTITY_MISMATCH",
|
|
54825
|
+
"BUNDLE_UNAVAILABLE",
|
|
54826
|
+
"BUNDLE_TOO_LARGE",
|
|
54827
|
+
"SKILL_NOT_SELECTED",
|
|
54828
|
+
"INVALID_SESSION",
|
|
54829
|
+
"SESSION_PARENT_NOT_FOUND",
|
|
54830
|
+
"SESSION_PARENT_MISMATCH",
|
|
54831
|
+
"SESSION_NOT_FOUND",
|
|
54832
|
+
"SESSION_GENERATION_CHANGED",
|
|
54833
|
+
"SESSION_GENERATION_EXHAUSTED",
|
|
54834
|
+
"SESSION_PARENT_CHANGED",
|
|
54835
|
+
"SESSION_RECEIPT_CHANGED",
|
|
54836
|
+
"SESSION_RECONCILIATION_REQUIRED",
|
|
54837
|
+
"SESSION_RENEWAL_TIMEOUT",
|
|
54838
|
+
"SESSION_CONTEXT_CONFLICT",
|
|
54839
|
+
"SESSION_RECONCILIATION_INCOMPLETE",
|
|
54840
|
+
"SESSION_WRITE_LOCKED",
|
|
54841
|
+
"SESSION_WRITE_LOCK_CHANGED",
|
|
54842
|
+
"SESSION_LOCK_RECOVERY_INCOMPLETE"
|
|
54843
|
+
]);
|
|
54844
|
+
HOOK_REFUSAL_REASON_CODES = Object.freeze([
|
|
54845
|
+
...CONTEXT_ERROR_CODES,
|
|
54846
|
+
"SKILLS_HOOK_FAILED",
|
|
54847
|
+
"SKILLS_HOOK_TIMEOUT",
|
|
54848
|
+
"SKILLS_HOOK_INVALID_RESPONSE"
|
|
54849
|
+
]);
|
|
54850
|
+
HookDiagnosticError = class HookDiagnosticError extends Error {
|
|
54851
|
+
code;
|
|
54852
|
+
stage;
|
|
54853
|
+
constructor(code, stage) {
|
|
54854
|
+
super("Skills hook operation failed");
|
|
54855
|
+
this.code = code;
|
|
54856
|
+
this.stage = stage;
|
|
54857
|
+
}
|
|
54858
|
+
};
|
|
54859
|
+
OPTIONAL_CONTEXT_FAILURES = new Set([
|
|
54860
|
+
"SKILLS_API_CREDENTIAL_UNAVAILABLE",
|
|
54861
|
+
"SKILLS_API_UNAVAILABLE",
|
|
54862
|
+
"SKILLS_API_UNAUTHORIZED",
|
|
54863
|
+
"SKILLS_API_FORBIDDEN",
|
|
54864
|
+
"SKILLS_API_RESOURCE_UNAVAILABLE",
|
|
54865
|
+
"CACHED_PROFILE_EXPIRED",
|
|
54866
|
+
"CACHED_PROFILE_MISSING",
|
|
54867
|
+
"CACHED_BUNDLE_MISSING",
|
|
54868
|
+
"SESSION_RECONCILIATION_REQUIRED",
|
|
54869
|
+
"SESSION_RENEWAL_TIMEOUT",
|
|
54870
|
+
"SESSION_WRITE_BUSY",
|
|
54871
|
+
"SESSION_PARENT_CHANGED",
|
|
54872
|
+
"SESSION_CONTEXT_CONFLICT",
|
|
54873
|
+
"SKILLS_HOOK_TIMEOUT"
|
|
54874
|
+
]);
|
|
54875
|
+
});
|
|
54876
|
+
|
|
54753
54877
|
// src/lib/agent-sumi.ts
|
|
54754
54878
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
54755
54879
|
import { accessSync as accessSync2, constants as constants12, existsSync as existsSync12, lstatSync as lstatSync19, realpathSync as realpathSync9, statSync as statSync9 } from "fs";
|
|
@@ -54883,24 +55007,33 @@ function sumiConfigPath(home, cwd2 = process.cwd(), env3 = process.env) {
|
|
|
54883
55007
|
return join28(sumiConfigDirectory(home, env3, cwd2), "sumi.json");
|
|
54884
55008
|
}
|
|
54885
55009
|
function renderSumiPlugin(command, profile) {
|
|
54886
|
-
return renderSumiPluginVersion(command, profile,
|
|
55010
|
+
return renderSumiPluginVersion(command, profile, 3);
|
|
54887
55011
|
}
|
|
54888
55012
|
function isManagedSumiPlugin(text2, command, profile) {
|
|
54889
|
-
return text2 === renderSumiPlugin(command, profile) || text2 === renderSumiPluginVersion(command, profile,
|
|
55013
|
+
return text2 === renderSumiPlugin(command, profile) || text2 === renderSumiPluginVersion(command, profile, 2) || text2 === renderSumiPluginVersion(command, profile, 1);
|
|
54890
55014
|
}
|
|
54891
|
-
function renderSumiPluginVersion(command, profile,
|
|
55015
|
+
function renderSumiPluginVersion(command, profile, version) {
|
|
55016
|
+
const legacy = version === 1;
|
|
54892
55017
|
const failure = (message) => legacy ? `new Error(${JSON.stringify(message)})` : "refusal()";
|
|
54893
55018
|
return `// Managed by @hasna/skills. Regenerate with skills hook install --agent sumi.
|
|
54894
55019
|
import { spawn } from "node:child_process";
|
|
54895
55020
|
const command = ${JSON.stringify(command)};
|
|
54896
55021
|
const profile = ${JSON.stringify(profile)};
|
|
54897
|
-
${legacy ? "" : `function refusal() {
|
|
54898
|
-
const error = new Error("Skills verification blocked this request. Review the Sumi Skills hook configuration, then retry.");
|
|
55022
|
+
${legacy ? "" : `function refusal(${version === 3 ? "reasonCode" : ""}) {
|
|
55023
|
+
const error = new Error("Skills verification blocked this request. Review the Sumi Skills hook configuration, then retry."${version === 3 ? ' + (reasonCode ? " [" + reasonCode + "]" : "")' : ""});
|
|
54899
55024
|
error.name = "SkillsHookRefusal";
|
|
54900
|
-
error.skillsHookRefusal = { version: 1, code: "SKILLS_HOOK_REFUSED" };
|
|
55025
|
+
error.skillsHookRefusal = { version: 1, code: "SKILLS_HOOK_REFUSED"${version === 3 ? ", ...(reasonCode ? { reasonCode } : {})" : ""} };
|
|
54901
55026
|
return error;
|
|
54902
55027
|
}
|
|
54903
|
-
`}
|
|
55028
|
+
`}${version === 3 ? `const reasonCodes = new Set(${JSON.stringify(HOOK_REFUSAL_REASON_CODES)});
|
|
55029
|
+
function refusedResult(result) {
|
|
55030
|
+
const reason = result.reason ?? result.stopReason ?? result.systemMessage;
|
|
55031
|
+
if (typeof reason !== "string" || reason.length > 16384) return refusal();
|
|
55032
|
+
if (reason.startsWith("NATIVE_SKILL_DRIFT:")) return refusal("NATIVE_SKILL_DRIFT");
|
|
55033
|
+
const code = /^Skills context is unavailable \\[([A-Z_]+)\\]/.exec(reason)?.[1];
|
|
55034
|
+
return refusal(reasonCodes.has(code) ? code : undefined);
|
|
55035
|
+
}
|
|
55036
|
+
` : ""}function context(input) {
|
|
54904
55037
|
return new Promise((resolve, reject) => {
|
|
54905
55038
|
const child = spawn(command, ["hook", "user-prompt", "--agent", "sumi", "--selection-profile", profile], { cwd: input.cwd, stdio: ["pipe", "pipe", "pipe"] });
|
|
54906
55039
|
const chunks = []; let bytes = 0, failure;
|
|
@@ -54915,12 +55048,12 @@ ${legacy ? "" : `function refusal() {
|
|
|
54915
55048
|
try {
|
|
54916
55049
|
const result = JSON.parse(Buffer.concat(chunks).toString("utf8"));
|
|
54917
55050
|
if (!result || typeof result !== "object" || Array.isArray(result)) throw ${failure("Invalid Skills hook response")};
|
|
54918
|
-
if (result.decision === "block" || result.continue === false) throw ${failure("Skills prompt hook refused")};
|
|
55051
|
+
if (result.decision === "block" || result.continue === false) throw ${version === 3 ? "refusedResult(result)" : failure("Skills prompt hook refused")};
|
|
54919
55052
|
if (Object.keys(result).length && (!result.hookSpecificOutput || result.hookSpecificOutput.hookEventName !== input.hook_event_name)) throw ${failure("Invalid Skills hook response")};
|
|
54920
55053
|
const text = result.hookSpecificOutput?.additionalContext ?? "";
|
|
54921
55054
|
if (typeof text !== "string") throw ${failure("Invalid Skills prompt context")};
|
|
54922
55055
|
resolve(text);
|
|
54923
|
-
} catch (error) { reject(${legacy ? "error" : "refusal()"}); }
|
|
55056
|
+
} catch (error) { reject(${legacy ? "error" : version === 3 ? 'error instanceof Error && error.name === "SkillsHookRefusal" ? error : refusal()' : "refusal()"}); }
|
|
54924
55057
|
});
|
|
54925
55058
|
child.stdin.end(JSON.stringify(input));
|
|
54926
55059
|
});
|
|
@@ -54953,9 +55086,13 @@ export default {
|
|
|
54953
55086
|
sessions.add(event.sessionID);
|
|
54954
55087
|
}));
|
|
54955
55088
|
registrations.push(await ctx.session.hook("context", async event => {
|
|
54956
|
-
|
|
55089
|
+
${version === 3 ? ` // Model messages also contain checkpoints, attachments and synthetic
|
|
55090
|
+
// user-role history, with no owner-prompt provenance. Prompt admission
|
|
55091
|
+
// already matched and persisted Skills; restore that verified state only.
|
|
55092
|
+
const prompt = "";
|
|
55093
|
+
` : ` const last = event.messages.findLast(message => message.role === "user");
|
|
54957
55094
|
const prompt = (last?.content ?? []).filter(part => part.type === "text").map(part => part.text).join("\\n");
|
|
54958
|
-
// Recheck on every primary request, including resumed and synthetic
|
|
55095
|
+
`} // Recheck on every primary request, including resumed and synthetic
|
|
54959
55096
|
// sessions. The CLI owns optional failures versus explicit refusals.
|
|
54960
55097
|
const text = await context(await input(event.sessionID, prompt, !sessions.has(event.sessionID)));
|
|
54961
55098
|
if (text) event.system.push({ type: "text", text });
|
|
@@ -54968,6 +55105,7 @@ export default {
|
|
|
54968
55105
|
}
|
|
54969
55106
|
var SUMI_PATH_SELECTORS, SUMI_PATH_KEYS, SUMI_PATH_OUTPUT_LIMIT, SumiPathResolverError, SUMI_SKILL_PERMISSIONS;
|
|
54970
55107
|
var init_agent_sumi = __esm(() => {
|
|
55108
|
+
init_hook_diagnostics();
|
|
54971
55109
|
SUMI_PATH_SELECTORS = ["SUMI_HOME", "SUMI_CONFIG_DIR", "XDG_DATA_HOME", "XDG_CACHE_HOME", "XDG_CONFIG_HOME", "XDG_STATE_HOME"];
|
|
54972
55110
|
SUMI_PATH_KEYS = ["data", "cache", "config", "state"];
|
|
54973
55111
|
SUMI_PATH_OUTPUT_LIMIT = 128 * 1024;
|
|
@@ -118034,124 +118172,6 @@ var init_codex_native_trust = __esm(() => {
|
|
|
118034
118172
|
HEX_DIGEST2 = /^[0-9a-f]{64}$/;
|
|
118035
118173
|
});
|
|
118036
118174
|
|
|
118037
|
-
// src/lib/hook-diagnostics.ts
|
|
118038
|
-
function hookChildError(stdout, stage) {
|
|
118039
|
-
let code2;
|
|
118040
|
-
if (stdout.length <= 16 * 1024) {
|
|
118041
|
-
try {
|
|
118042
|
-
code2 = JSON.parse(stdout)?.error?.code;
|
|
118043
|
-
} catch {}
|
|
118044
|
-
}
|
|
118045
|
-
return new HookDiagnosticError(typeof code2 === "string" && CONTEXT_ERROR_CODES.has(code2) ? code2 : "SKILLS_CONTEXT_FAILED", stage);
|
|
118046
|
-
}
|
|
118047
|
-
function hookFailureReason(error61, profileId) {
|
|
118048
|
-
const owned = error61 instanceof HookDiagnosticError;
|
|
118049
|
-
const code2 = owned && (CONTEXT_ERROR_CODES.has(error61.code) || ["SKILLS_HOOK_TIMEOUT", "SKILLS_HOOK_INVALID_RESPONSE"].includes(error61.code)) ? error61.code : "SKILLS_HOOK_FAILED";
|
|
118050
|
-
const profile = typeof profileId === "string" && /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(profileId) && !profileId.includes("..") ? profileId : undefined;
|
|
118051
|
-
const identity6 = [profile ? `profile=${profile}` : undefined, owned ? `stage=${error61.stage}` : undefined].filter(Boolean).join(", ");
|
|
118052
|
-
const prefix = `Skills context is unavailable [${code2}]${identity6 ? ` (${identity6})` : ""}.`;
|
|
118053
|
-
if (code2 === "SKILLS_STORAGE_FULL") {
|
|
118054
|
-
return `${prefix} The filesystem is full or its quota is exhausted. Free space through your approved recoverable cleanup process, then retry the same session. Do not delete session receipts or change credentials to bypass this failure.`;
|
|
118055
|
-
}
|
|
118056
|
-
if (code2 === "PROFILE_LOCK_MISMATCH") {
|
|
118057
|
-
return `${prefix} This session or project is pinned to a different profile. Inspect the session with skills sessions show <session-id> --json; review skills sessions reconcile for an intentional session change, or explicit project sync for a project change. Sync alone does not change that pin.`;
|
|
118058
|
-
}
|
|
118059
|
-
if (code2 === "SESSION_RECONCILIATION_REQUIRED") {
|
|
118060
|
-
return `${prefix} A loaded skill changed or was removed. Inspect this session with skills sessions show <session-id> --json, then review skills sessions reconcile for an intentional change. Sync alone does not change this pin. A refusal is re-checked with the authority within five minutes, or sooner after the receipt or the synced profile revision changes.`;
|
|
118061
|
-
}
|
|
118062
|
-
if (code2 === "SESSION_RENEWAL_TIMEOUT") {
|
|
118063
|
-
return `${prefix} Renewing this session's expired authorization did not finish within the hook's renewal window; no request was refused. Sync and diagnosis do not change this window. The existing pin is unchanged and the next prompt retries the renewal. If it keeps recurring, inspect the session with skills sessions show <session-id> --json and review skills sessions reconcile.`;
|
|
118064
|
-
}
|
|
118065
|
-
if (code2.startsWith("SESSION_")) {
|
|
118066
|
-
return `${prefix} Inspect the session with skills sessions show <session-id> --json and review its parent or concurrent writer before retrying. Do not remove or rewrite its receipt to bypass this refusal.`;
|
|
118067
|
-
}
|
|
118068
|
-
if (["PROFILE_IDENTITY_MISMATCH", "BUNDLE_DIGEST_MISMATCH", "BUNDLE_IDENTITY_MISMATCH", "INVALID_RECEIPT", "INVALID_CACHE_FILE", "UNSAFE_CACHE_PATH"].includes(code2)) {
|
|
118069
|
-
return `${prefix} Verify the selected authority and receipt integrity before repairing the profile; do not bypass verification or substitute another cache.`;
|
|
118070
|
-
}
|
|
118071
|
-
const selected = profile ?? "<id>";
|
|
118072
|
-
return `${prefix} Run skills sync --selection-profile ${selected} --json, then diagnose with skills context --stdin --json --cached --selection-profile ${selected} using the same hook session and working-directory fields. Do not include credentials in diagnostic input.`;
|
|
118073
|
-
}
|
|
118074
|
-
function isOptionalHookContextFailure(error61) {
|
|
118075
|
-
return error61 instanceof HookDiagnosticError && OPTIONAL_CONTEXT_FAILURES.has(error61.code);
|
|
118076
|
-
}
|
|
118077
|
-
function hookUnavailableContext(error61, profileId) {
|
|
118078
|
-
return `${hookFailureReason(error61, profileId)} No Skills instructions were delivered by this hook. Ordinary work may continue. Do not perform actions that depend on unavailable Skills instructions, including previously loaded instructions. Explicit skills load, context and run still require verification. Do not substitute native, unmanaged or stale payloads.`;
|
|
118079
|
-
}
|
|
118080
|
-
var CONTEXT_ERROR_CODES, HookDiagnosticError, OPTIONAL_CONTEXT_FAILURES;
|
|
118081
|
-
var init_hook_diagnostics = __esm(() => {
|
|
118082
|
-
CONTEXT_ERROR_CODES = new Set([
|
|
118083
|
-
"SKILLS_API_CREDENTIAL_UNAVAILABLE",
|
|
118084
|
-
"SKILLS_API_UNAVAILABLE",
|
|
118085
|
-
"SKILLS_API_UNAUTHORIZED",
|
|
118086
|
-
"SKILLS_API_FORBIDDEN",
|
|
118087
|
-
"SKILLS_API_RESOURCE_UNAVAILABLE",
|
|
118088
|
-
"SESSION_WRITE_BUSY",
|
|
118089
|
-
"SKILLS_CONTEXT_FAILED",
|
|
118090
|
-
"SKILLS_STORAGE_FULL",
|
|
118091
|
-
"PROFILE_LOCK_MISMATCH",
|
|
118092
|
-
"PROFILE_IDENTITY_MISMATCH",
|
|
118093
|
-
"PROFILE_APPLIED_REPORT_FAILED",
|
|
118094
|
-
"CACHED_AUTHORITY_REQUIRED",
|
|
118095
|
-
"CACHED_PROFILE_EXPIRED",
|
|
118096
|
-
"CACHED_PROFILE_MISSING",
|
|
118097
|
-
"CACHED_BUNDLE_MISSING",
|
|
118098
|
-
"INVALID_CONTEXT_INPUT",
|
|
118099
|
-
"CONTEXT_INPUT_TOO_LARGE",
|
|
118100
|
-
"INVALID_CONTEXT_BUDGET",
|
|
118101
|
-
"INVALID_SELECTION",
|
|
118102
|
-
"INVALID_SELECTION_ALIASES",
|
|
118103
|
-
"INVALID_RECEIPT",
|
|
118104
|
-
"INVALID_CACHE_FILE",
|
|
118105
|
-
"UNSAFE_CACHE_PATH",
|
|
118106
|
-
"RECEIPT_TOO_LARGE",
|
|
118107
|
-
"BUNDLE_DIGEST_MISMATCH",
|
|
118108
|
-
"BUNDLE_IDENTITY_MISMATCH",
|
|
118109
|
-
"BUNDLE_UNAVAILABLE",
|
|
118110
|
-
"BUNDLE_TOO_LARGE",
|
|
118111
|
-
"SKILL_NOT_SELECTED",
|
|
118112
|
-
"INVALID_SESSION",
|
|
118113
|
-
"SESSION_PARENT_NOT_FOUND",
|
|
118114
|
-
"SESSION_PARENT_MISMATCH",
|
|
118115
|
-
"SESSION_NOT_FOUND",
|
|
118116
|
-
"SESSION_GENERATION_CHANGED",
|
|
118117
|
-
"SESSION_GENERATION_EXHAUSTED",
|
|
118118
|
-
"SESSION_PARENT_CHANGED",
|
|
118119
|
-
"SESSION_RECEIPT_CHANGED",
|
|
118120
|
-
"SESSION_RECONCILIATION_REQUIRED",
|
|
118121
|
-
"SESSION_RENEWAL_TIMEOUT",
|
|
118122
|
-
"SESSION_CONTEXT_CONFLICT",
|
|
118123
|
-
"SESSION_RECONCILIATION_INCOMPLETE",
|
|
118124
|
-
"SESSION_WRITE_LOCKED",
|
|
118125
|
-
"SESSION_WRITE_LOCK_CHANGED",
|
|
118126
|
-
"SESSION_LOCK_RECOVERY_INCOMPLETE"
|
|
118127
|
-
]);
|
|
118128
|
-
HookDiagnosticError = class HookDiagnosticError extends Error {
|
|
118129
|
-
code;
|
|
118130
|
-
stage;
|
|
118131
|
-
constructor(code2, stage) {
|
|
118132
|
-
super("Skills hook operation failed");
|
|
118133
|
-
this.code = code2;
|
|
118134
|
-
this.stage = stage;
|
|
118135
|
-
}
|
|
118136
|
-
};
|
|
118137
|
-
OPTIONAL_CONTEXT_FAILURES = new Set([
|
|
118138
|
-
"SKILLS_API_CREDENTIAL_UNAVAILABLE",
|
|
118139
|
-
"SKILLS_API_UNAVAILABLE",
|
|
118140
|
-
"SKILLS_API_UNAUTHORIZED",
|
|
118141
|
-
"SKILLS_API_FORBIDDEN",
|
|
118142
|
-
"SKILLS_API_RESOURCE_UNAVAILABLE",
|
|
118143
|
-
"CACHED_PROFILE_EXPIRED",
|
|
118144
|
-
"CACHED_PROFILE_MISSING",
|
|
118145
|
-
"CACHED_BUNDLE_MISSING",
|
|
118146
|
-
"SESSION_RECONCILIATION_REQUIRED",
|
|
118147
|
-
"SESSION_RENEWAL_TIMEOUT",
|
|
118148
|
-
"SESSION_WRITE_BUSY",
|
|
118149
|
-
"SESSION_PARENT_CHANGED",
|
|
118150
|
-
"SESSION_CONTEXT_CONFLICT",
|
|
118151
|
-
"SKILLS_HOOK_TIMEOUT"
|
|
118152
|
-
]);
|
|
118153
|
-
});
|
|
118154
|
-
|
|
118155
118175
|
// src/lib/claude-prospective-review.ts
|
|
118156
118176
|
import { createHash as createHash43 } from "crypto";
|
|
118157
118177
|
import { lstatSync as lstatSync41, readFileSync as readFileSync45, readdirSync as readdirSync25, realpathSync as realpathSync22 } from "fs";
|
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.65",
|
|
27
27
|
description: "Skills library for AI coding agents",
|
|
28
28
|
type: "module",
|
|
29
29
|
bin: {
|
|
@@ -34337,6 +34337,79 @@ var init_agent_bridge = __esm(() => {
|
|
|
34337
34337
|
// src/lib/agent-codex.ts
|
|
34338
34338
|
var init_agent_codex = () => {};
|
|
34339
34339
|
|
|
34340
|
+
// src/lib/hook-diagnostics.ts
|
|
34341
|
+
var CONTEXT_ERROR_CODES, HOOK_REFUSAL_REASON_CODES, OPTIONAL_CONTEXT_FAILURES;
|
|
34342
|
+
var init_hook_diagnostics = __esm(() => {
|
|
34343
|
+
CONTEXT_ERROR_CODES = new Set([
|
|
34344
|
+
"SKILLS_API_CREDENTIAL_UNAVAILABLE",
|
|
34345
|
+
"SKILLS_API_UNAVAILABLE",
|
|
34346
|
+
"SKILLS_API_UNAUTHORIZED",
|
|
34347
|
+
"SKILLS_API_FORBIDDEN",
|
|
34348
|
+
"SKILLS_API_RESOURCE_UNAVAILABLE",
|
|
34349
|
+
"SESSION_WRITE_BUSY",
|
|
34350
|
+
"SKILLS_CONTEXT_FAILED",
|
|
34351
|
+
"SKILLS_STORAGE_FULL",
|
|
34352
|
+
"PROFILE_LOCK_MISMATCH",
|
|
34353
|
+
"PROFILE_IDENTITY_MISMATCH",
|
|
34354
|
+
"PROFILE_APPLIED_REPORT_FAILED",
|
|
34355
|
+
"CACHED_AUTHORITY_REQUIRED",
|
|
34356
|
+
"CACHED_PROFILE_EXPIRED",
|
|
34357
|
+
"CACHED_PROFILE_MISSING",
|
|
34358
|
+
"CACHED_BUNDLE_MISSING",
|
|
34359
|
+
"INVALID_CONTEXT_INPUT",
|
|
34360
|
+
"CONTEXT_INPUT_TOO_LARGE",
|
|
34361
|
+
"INVALID_CONTEXT_BUDGET",
|
|
34362
|
+
"INVALID_SELECTION",
|
|
34363
|
+
"INVALID_SELECTION_ALIASES",
|
|
34364
|
+
"INVALID_RECEIPT",
|
|
34365
|
+
"INVALID_CACHE_FILE",
|
|
34366
|
+
"UNSAFE_CACHE_PATH",
|
|
34367
|
+
"RECEIPT_TOO_LARGE",
|
|
34368
|
+
"BUNDLE_DIGEST_MISMATCH",
|
|
34369
|
+
"BUNDLE_IDENTITY_MISMATCH",
|
|
34370
|
+
"BUNDLE_UNAVAILABLE",
|
|
34371
|
+
"BUNDLE_TOO_LARGE",
|
|
34372
|
+
"SKILL_NOT_SELECTED",
|
|
34373
|
+
"INVALID_SESSION",
|
|
34374
|
+
"SESSION_PARENT_NOT_FOUND",
|
|
34375
|
+
"SESSION_PARENT_MISMATCH",
|
|
34376
|
+
"SESSION_NOT_FOUND",
|
|
34377
|
+
"SESSION_GENERATION_CHANGED",
|
|
34378
|
+
"SESSION_GENERATION_EXHAUSTED",
|
|
34379
|
+
"SESSION_PARENT_CHANGED",
|
|
34380
|
+
"SESSION_RECEIPT_CHANGED",
|
|
34381
|
+
"SESSION_RECONCILIATION_REQUIRED",
|
|
34382
|
+
"SESSION_RENEWAL_TIMEOUT",
|
|
34383
|
+
"SESSION_CONTEXT_CONFLICT",
|
|
34384
|
+
"SESSION_RECONCILIATION_INCOMPLETE",
|
|
34385
|
+
"SESSION_WRITE_LOCKED",
|
|
34386
|
+
"SESSION_WRITE_LOCK_CHANGED",
|
|
34387
|
+
"SESSION_LOCK_RECOVERY_INCOMPLETE"
|
|
34388
|
+
]);
|
|
34389
|
+
HOOK_REFUSAL_REASON_CODES = Object.freeze([
|
|
34390
|
+
...CONTEXT_ERROR_CODES,
|
|
34391
|
+
"SKILLS_HOOK_FAILED",
|
|
34392
|
+
"SKILLS_HOOK_TIMEOUT",
|
|
34393
|
+
"SKILLS_HOOK_INVALID_RESPONSE"
|
|
34394
|
+
]);
|
|
34395
|
+
OPTIONAL_CONTEXT_FAILURES = new Set([
|
|
34396
|
+
"SKILLS_API_CREDENTIAL_UNAVAILABLE",
|
|
34397
|
+
"SKILLS_API_UNAVAILABLE",
|
|
34398
|
+
"SKILLS_API_UNAUTHORIZED",
|
|
34399
|
+
"SKILLS_API_FORBIDDEN",
|
|
34400
|
+
"SKILLS_API_RESOURCE_UNAVAILABLE",
|
|
34401
|
+
"CACHED_PROFILE_EXPIRED",
|
|
34402
|
+
"CACHED_PROFILE_MISSING",
|
|
34403
|
+
"CACHED_BUNDLE_MISSING",
|
|
34404
|
+
"SESSION_RECONCILIATION_REQUIRED",
|
|
34405
|
+
"SESSION_RENEWAL_TIMEOUT",
|
|
34406
|
+
"SESSION_WRITE_BUSY",
|
|
34407
|
+
"SESSION_PARENT_CHANGED",
|
|
34408
|
+
"SESSION_CONTEXT_CONFLICT",
|
|
34409
|
+
"SKILLS_HOOK_TIMEOUT"
|
|
34410
|
+
]);
|
|
34411
|
+
});
|
|
34412
|
+
|
|
34340
34413
|
// src/lib/agent-sumi.ts
|
|
34341
34414
|
import { spawnSync as spawnSync4 } from "child_process";
|
|
34342
34415
|
import { accessSync, constants as constants9, existsSync as existsSync8, lstatSync as lstatSync15, realpathSync as realpathSync7, statSync as statSync7 } from "fs";
|
|
@@ -34464,6 +34537,7 @@ function sumiConfigPath(home, cwd = process.cwd(), env = process.env) {
|
|
|
34464
34537
|
}
|
|
34465
34538
|
var SUMI_PATH_SELECTORS, SUMI_PATH_KEYS, SUMI_PATH_OUTPUT_LIMIT, SumiPathResolverError;
|
|
34466
34539
|
var init_agent_sumi = __esm(() => {
|
|
34540
|
+
init_hook_diagnostics();
|
|
34467
34541
|
SUMI_PATH_SELECTORS = ["SUMI_HOME", "SUMI_CONFIG_DIR", "XDG_DATA_HOME", "XDG_CACHE_HOME", "XDG_CONFIG_HOME", "XDG_STATE_HOME"];
|
|
34468
34542
|
SUMI_PATH_KEYS = ["data", "cache", "config", "state"];
|
|
34469
34543
|
SUMI_PATH_OUTPUT_LIMIT = 128 * 1024;
|
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.65",
|
|
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.65",
|
|
24147
24147
|
description: "Skills library for AI coding agents",
|
|
24148
24148
|
type: "module",
|
|
24149
24149
|
bin: {
|
package/dist/index.js
CHANGED
|
@@ -11888,7 +11888,7 @@ function replaceSkillSession(expected, replacement, plan, options = {}, beforeCo
|
|
|
11888
11888
|
});
|
|
11889
11889
|
}
|
|
11890
11890
|
// package.json
|
|
11891
|
-
var version = "0.10.
|
|
11891
|
+
var version = "0.10.65";
|
|
11892
11892
|
var package_default = {
|
|
11893
11893
|
name: "@hasna/skills",
|
|
11894
11894
|
version,
|
|
@@ -17141,6 +17141,78 @@ function disableCodexBundledSkills(text2) {
|
|
|
17141
17141
|
import { spawnSync as spawnSync4 } from "child_process";
|
|
17142
17142
|
import { accessSync as accessSync2, constants as constants11, existsSync as existsSync8, lstatSync as lstatSync19, realpathSync as realpathSync10, statSync as statSync9 } from "fs";
|
|
17143
17143
|
import { delimiter as delimiter2, isAbsolute as isAbsolute15, join as join25, resolve as resolve15 } from "path";
|
|
17144
|
+
|
|
17145
|
+
// src/lib/hook-diagnostics.ts
|
|
17146
|
+
var CONTEXT_ERROR_CODES = new Set([
|
|
17147
|
+
"SKILLS_API_CREDENTIAL_UNAVAILABLE",
|
|
17148
|
+
"SKILLS_API_UNAVAILABLE",
|
|
17149
|
+
"SKILLS_API_UNAUTHORIZED",
|
|
17150
|
+
"SKILLS_API_FORBIDDEN",
|
|
17151
|
+
"SKILLS_API_RESOURCE_UNAVAILABLE",
|
|
17152
|
+
"SESSION_WRITE_BUSY",
|
|
17153
|
+
"SKILLS_CONTEXT_FAILED",
|
|
17154
|
+
"SKILLS_STORAGE_FULL",
|
|
17155
|
+
"PROFILE_LOCK_MISMATCH",
|
|
17156
|
+
"PROFILE_IDENTITY_MISMATCH",
|
|
17157
|
+
"PROFILE_APPLIED_REPORT_FAILED",
|
|
17158
|
+
"CACHED_AUTHORITY_REQUIRED",
|
|
17159
|
+
"CACHED_PROFILE_EXPIRED",
|
|
17160
|
+
"CACHED_PROFILE_MISSING",
|
|
17161
|
+
"CACHED_BUNDLE_MISSING",
|
|
17162
|
+
"INVALID_CONTEXT_INPUT",
|
|
17163
|
+
"CONTEXT_INPUT_TOO_LARGE",
|
|
17164
|
+
"INVALID_CONTEXT_BUDGET",
|
|
17165
|
+
"INVALID_SELECTION",
|
|
17166
|
+
"INVALID_SELECTION_ALIASES",
|
|
17167
|
+
"INVALID_RECEIPT",
|
|
17168
|
+
"INVALID_CACHE_FILE",
|
|
17169
|
+
"UNSAFE_CACHE_PATH",
|
|
17170
|
+
"RECEIPT_TOO_LARGE",
|
|
17171
|
+
"BUNDLE_DIGEST_MISMATCH",
|
|
17172
|
+
"BUNDLE_IDENTITY_MISMATCH",
|
|
17173
|
+
"BUNDLE_UNAVAILABLE",
|
|
17174
|
+
"BUNDLE_TOO_LARGE",
|
|
17175
|
+
"SKILL_NOT_SELECTED",
|
|
17176
|
+
"INVALID_SESSION",
|
|
17177
|
+
"SESSION_PARENT_NOT_FOUND",
|
|
17178
|
+
"SESSION_PARENT_MISMATCH",
|
|
17179
|
+
"SESSION_NOT_FOUND",
|
|
17180
|
+
"SESSION_GENERATION_CHANGED",
|
|
17181
|
+
"SESSION_GENERATION_EXHAUSTED",
|
|
17182
|
+
"SESSION_PARENT_CHANGED",
|
|
17183
|
+
"SESSION_RECEIPT_CHANGED",
|
|
17184
|
+
"SESSION_RECONCILIATION_REQUIRED",
|
|
17185
|
+
"SESSION_RENEWAL_TIMEOUT",
|
|
17186
|
+
"SESSION_CONTEXT_CONFLICT",
|
|
17187
|
+
"SESSION_RECONCILIATION_INCOMPLETE",
|
|
17188
|
+
"SESSION_WRITE_LOCKED",
|
|
17189
|
+
"SESSION_WRITE_LOCK_CHANGED",
|
|
17190
|
+
"SESSION_LOCK_RECOVERY_INCOMPLETE"
|
|
17191
|
+
]);
|
|
17192
|
+
var HOOK_REFUSAL_REASON_CODES = Object.freeze([
|
|
17193
|
+
...CONTEXT_ERROR_CODES,
|
|
17194
|
+
"SKILLS_HOOK_FAILED",
|
|
17195
|
+
"SKILLS_HOOK_TIMEOUT",
|
|
17196
|
+
"SKILLS_HOOK_INVALID_RESPONSE"
|
|
17197
|
+
]);
|
|
17198
|
+
var OPTIONAL_CONTEXT_FAILURES = new Set([
|
|
17199
|
+
"SKILLS_API_CREDENTIAL_UNAVAILABLE",
|
|
17200
|
+
"SKILLS_API_UNAVAILABLE",
|
|
17201
|
+
"SKILLS_API_UNAUTHORIZED",
|
|
17202
|
+
"SKILLS_API_FORBIDDEN",
|
|
17203
|
+
"SKILLS_API_RESOURCE_UNAVAILABLE",
|
|
17204
|
+
"CACHED_PROFILE_EXPIRED",
|
|
17205
|
+
"CACHED_PROFILE_MISSING",
|
|
17206
|
+
"CACHED_BUNDLE_MISSING",
|
|
17207
|
+
"SESSION_RECONCILIATION_REQUIRED",
|
|
17208
|
+
"SESSION_RENEWAL_TIMEOUT",
|
|
17209
|
+
"SESSION_WRITE_BUSY",
|
|
17210
|
+
"SESSION_PARENT_CHANGED",
|
|
17211
|
+
"SESSION_CONTEXT_CONFLICT",
|
|
17212
|
+
"SKILLS_HOOK_TIMEOUT"
|
|
17213
|
+
]);
|
|
17214
|
+
|
|
17215
|
+
// src/lib/agent-sumi.ts
|
|
17144
17216
|
var SUMI_PATH_SELECTORS = ["SUMI_HOME", "SUMI_CONFIG_DIR", "XDG_DATA_HOME", "XDG_CACHE_HOME", "XDG_CONFIG_HOME", "XDG_STATE_HOME"];
|
|
17145
17217
|
var SUMI_PATH_KEYS = ["data", "cache", "config", "state"];
|
|
17146
17218
|
var SUMI_PATH_OUTPUT_LIMIT = 128 * 1024;
|
|
@@ -17282,24 +17354,33 @@ function sumiConfigPath(home, cwd = process.cwd(), env = process.env) {
|
|
|
17282
17354
|
return join25(sumiConfigDirectory(home, env, cwd), "sumi.json");
|
|
17283
17355
|
}
|
|
17284
17356
|
function renderSumiPlugin(command, profile) {
|
|
17285
|
-
return renderSumiPluginVersion(command, profile,
|
|
17357
|
+
return renderSumiPluginVersion(command, profile, 3);
|
|
17286
17358
|
}
|
|
17287
17359
|
function isManagedSumiPlugin(text2, command, profile) {
|
|
17288
|
-
return text2 === renderSumiPlugin(command, profile) || text2 === renderSumiPluginVersion(command, profile,
|
|
17360
|
+
return text2 === renderSumiPlugin(command, profile) || text2 === renderSumiPluginVersion(command, profile, 2) || text2 === renderSumiPluginVersion(command, profile, 1);
|
|
17289
17361
|
}
|
|
17290
|
-
function renderSumiPluginVersion(command, profile,
|
|
17362
|
+
function renderSumiPluginVersion(command, profile, version2) {
|
|
17363
|
+
const legacy = version2 === 1;
|
|
17291
17364
|
const failure = (message) => legacy ? `new Error(${JSON.stringify(message)})` : "refusal()";
|
|
17292
17365
|
return `// Managed by @hasna/skills. Regenerate with skills hook install --agent sumi.
|
|
17293
17366
|
import { spawn } from "node:child_process";
|
|
17294
17367
|
const command = ${JSON.stringify(command)};
|
|
17295
17368
|
const profile = ${JSON.stringify(profile)};
|
|
17296
|
-
${legacy ? "" : `function refusal() {
|
|
17297
|
-
const error = new Error("Skills verification blocked this request. Review the Sumi Skills hook configuration, then retry.");
|
|
17369
|
+
${legacy ? "" : `function refusal(${version2 === 3 ? "reasonCode" : ""}) {
|
|
17370
|
+
const error = new Error("Skills verification blocked this request. Review the Sumi Skills hook configuration, then retry."${version2 === 3 ? ' + (reasonCode ? " [" + reasonCode + "]" : "")' : ""});
|
|
17298
17371
|
error.name = "SkillsHookRefusal";
|
|
17299
|
-
error.skillsHookRefusal = { version: 1, code: "SKILLS_HOOK_REFUSED" };
|
|
17372
|
+
error.skillsHookRefusal = { version: 1, code: "SKILLS_HOOK_REFUSED"${version2 === 3 ? ", ...(reasonCode ? { reasonCode } : {})" : ""} };
|
|
17300
17373
|
return error;
|
|
17301
17374
|
}
|
|
17302
|
-
`}
|
|
17375
|
+
`}${version2 === 3 ? `const reasonCodes = new Set(${JSON.stringify(HOOK_REFUSAL_REASON_CODES)});
|
|
17376
|
+
function refusedResult(result) {
|
|
17377
|
+
const reason = result.reason ?? result.stopReason ?? result.systemMessage;
|
|
17378
|
+
if (typeof reason !== "string" || reason.length > 16384) return refusal();
|
|
17379
|
+
if (reason.startsWith("NATIVE_SKILL_DRIFT:")) return refusal("NATIVE_SKILL_DRIFT");
|
|
17380
|
+
const code = /^Skills context is unavailable \\[([A-Z_]+)\\]/.exec(reason)?.[1];
|
|
17381
|
+
return refusal(reasonCodes.has(code) ? code : undefined);
|
|
17382
|
+
}
|
|
17383
|
+
` : ""}function context(input) {
|
|
17303
17384
|
return new Promise((resolve, reject) => {
|
|
17304
17385
|
const child = spawn(command, ["hook", "user-prompt", "--agent", "sumi", "--selection-profile", profile], { cwd: input.cwd, stdio: ["pipe", "pipe", "pipe"] });
|
|
17305
17386
|
const chunks = []; let bytes = 0, failure;
|
|
@@ -17314,12 +17395,12 @@ ${legacy ? "" : `function refusal() {
|
|
|
17314
17395
|
try {
|
|
17315
17396
|
const result = JSON.parse(Buffer.concat(chunks).toString("utf8"));
|
|
17316
17397
|
if (!result || typeof result !== "object" || Array.isArray(result)) throw ${failure("Invalid Skills hook response")};
|
|
17317
|
-
if (result.decision === "block" || result.continue === false) throw ${failure("Skills prompt hook refused")};
|
|
17398
|
+
if (result.decision === "block" || result.continue === false) throw ${version2 === 3 ? "refusedResult(result)" : failure("Skills prompt hook refused")};
|
|
17318
17399
|
if (Object.keys(result).length && (!result.hookSpecificOutput || result.hookSpecificOutput.hookEventName !== input.hook_event_name)) throw ${failure("Invalid Skills hook response")};
|
|
17319
17400
|
const text = result.hookSpecificOutput?.additionalContext ?? "";
|
|
17320
17401
|
if (typeof text !== "string") throw ${failure("Invalid Skills prompt context")};
|
|
17321
17402
|
resolve(text);
|
|
17322
|
-
} catch (error) { reject(${legacy ? "error" : "refusal()"}); }
|
|
17403
|
+
} catch (error) { reject(${legacy ? "error" : version2 === 3 ? 'error instanceof Error && error.name === "SkillsHookRefusal" ? error : refusal()' : "refusal()"}); }
|
|
17323
17404
|
});
|
|
17324
17405
|
child.stdin.end(JSON.stringify(input));
|
|
17325
17406
|
});
|
|
@@ -17352,9 +17433,13 @@ export default {
|
|
|
17352
17433
|
sessions.add(event.sessionID);
|
|
17353
17434
|
}));
|
|
17354
17435
|
registrations.push(await ctx.session.hook("context", async event => {
|
|
17355
|
-
|
|
17436
|
+
${version2 === 3 ? ` // Model messages also contain checkpoints, attachments and synthetic
|
|
17437
|
+
// user-role history, with no owner-prompt provenance. Prompt admission
|
|
17438
|
+
// already matched and persisted Skills; restore that verified state only.
|
|
17439
|
+
const prompt = "";
|
|
17440
|
+
` : ` const last = event.messages.findLast(message => message.role === "user");
|
|
17356
17441
|
const prompt = (last?.content ?? []).filter(part => part.type === "text").map(part => part.text).join("\\n");
|
|
17357
|
-
// Recheck on every primary request, including resumed and synthetic
|
|
17442
|
+
`} // Recheck on every primary request, including resumed and synthetic
|
|
17358
17443
|
// sessions. The CLI owns optional failures versus explicit refusals.
|
|
17359
17444
|
const text = await context(await input(event.sessionID, prompt, !sessions.has(event.sessionID)));
|
|
17360
17445
|
if (text) event.system.push({ type: "text", text });
|
|
@@ -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,
|
|
@@ -2864,6 +2864,78 @@ function detectedIntegrationAgents(options = {}) {
|
|
|
2864
2864
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
2865
2865
|
import { accessSync, constants as constants4, existsSync as existsSync4, lstatSync as lstatSync5, realpathSync, statSync as statSync2 } from "fs";
|
|
2866
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
|
|
2867
2939
|
var SUMI_PATH_SELECTORS = ["SUMI_HOME", "SUMI_CONFIG_DIR", "XDG_DATA_HOME", "XDG_CACHE_HOME", "XDG_CONFIG_HOME", "XDG_STATE_HOME"];
|
|
2868
2940
|
var SUMI_PATH_KEYS = ["data", "cache", "config", "state"];
|
|
2869
2941
|
var SUMI_PATH_OUTPUT_LIMIT = 128 * 1024;
|
|
@@ -3005,24 +3077,33 @@ function sumiConfigPath(home, cwd = process.cwd(), env = process.env) {
|
|
|
3005
3077
|
return join8(sumiConfigDirectory(home, env, cwd), "sumi.json");
|
|
3006
3078
|
}
|
|
3007
3079
|
function renderSumiPlugin(command, profile) {
|
|
3008
|
-
return renderSumiPluginVersion(command, profile,
|
|
3080
|
+
return renderSumiPluginVersion(command, profile, 3);
|
|
3009
3081
|
}
|
|
3010
3082
|
function isManagedSumiPlugin(text2, command, profile) {
|
|
3011
|
-
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);
|
|
3012
3084
|
}
|
|
3013
|
-
function renderSumiPluginVersion(command, profile,
|
|
3085
|
+
function renderSumiPluginVersion(command, profile, version2) {
|
|
3086
|
+
const legacy = version2 === 1;
|
|
3014
3087
|
const failure = (message) => legacy ? `new Error(${JSON.stringify(message)})` : "refusal()";
|
|
3015
3088
|
return `// Managed by @hasna/skills. Regenerate with skills hook install --agent sumi.
|
|
3016
3089
|
import { spawn } from "node:child_process";
|
|
3017
3090
|
const command = ${JSON.stringify(command)};
|
|
3018
3091
|
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.");
|
|
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 + "]" : "")' : ""});
|
|
3021
3094
|
error.name = "SkillsHookRefusal";
|
|
3022
|
-
error.skillsHookRefusal = { version: 1, code: "SKILLS_HOOK_REFUSED" };
|
|
3095
|
+
error.skillsHookRefusal = { version: 1, code: "SKILLS_HOOK_REFUSED"${version2 === 3 ? ", ...(reasonCode ? { reasonCode } : {})" : ""} };
|
|
3023
3096
|
return error;
|
|
3024
3097
|
}
|
|
3025
|
-
`}
|
|
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) {
|
|
3026
3107
|
return new Promise((resolve, reject) => {
|
|
3027
3108
|
const child = spawn(command, ["hook", "user-prompt", "--agent", "sumi", "--selection-profile", profile], { cwd: input.cwd, stdio: ["pipe", "pipe", "pipe"] });
|
|
3028
3109
|
const chunks = []; let bytes = 0, failure;
|
|
@@ -3037,12 +3118,12 @@ ${legacy ? "" : `function refusal() {
|
|
|
3037
3118
|
try {
|
|
3038
3119
|
const result = JSON.parse(Buffer.concat(chunks).toString("utf8"));
|
|
3039
3120
|
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")};
|
|
3121
|
+
if (result.decision === "block" || result.continue === false) throw ${version2 === 3 ? "refusedResult(result)" : failure("Skills prompt hook refused")};
|
|
3041
3122
|
if (Object.keys(result).length && (!result.hookSpecificOutput || result.hookSpecificOutput.hookEventName !== input.hook_event_name)) throw ${failure("Invalid Skills hook response")};
|
|
3042
3123
|
const text = result.hookSpecificOutput?.additionalContext ?? "";
|
|
3043
3124
|
if (typeof text !== "string") throw ${failure("Invalid Skills prompt context")};
|
|
3044
3125
|
resolve(text);
|
|
3045
|
-
} catch (error) { reject(${legacy ? "error" : "refusal()"}); }
|
|
3126
|
+
} catch (error) { reject(${legacy ? "error" : version2 === 3 ? 'error instanceof Error && error.name === "SkillsHookRefusal" ? error : refusal()' : "refusal()"}); }
|
|
3046
3127
|
});
|
|
3047
3128
|
child.stdin.end(JSON.stringify(input));
|
|
3048
3129
|
});
|
|
@@ -3075,9 +3156,13 @@ export default {
|
|
|
3075
3156
|
sessions.add(event.sessionID);
|
|
3076
3157
|
}));
|
|
3077
3158
|
registrations.push(await ctx.session.hook("context", async event => {
|
|
3078
|
-
|
|
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");
|
|
3079
3164
|
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
|
|
3165
|
+
`} // Recheck on every primary request, including resumed and synthetic
|
|
3081
3166
|
// sessions. The CLI owns optional failures versus explicit refusals.
|
|
3082
3167
|
const text = await context(await input(event.sessionID, prompt, !sessions.has(event.sessionID)));
|
|
3083
3168
|
if (text) event.system.push({ type: "text", text });
|
package/dist/sdk/index.js
CHANGED
|
@@ -32371,7 +32371,7 @@ var init_event_sink = __esm(() => {
|
|
|
32371
32371
|
init_dist();
|
|
32372
32372
|
});
|
|
32373
32373
|
// package.json
|
|
32374
|
-
var version = "0.10.
|
|
32374
|
+
var version = "0.10.65";
|
|
32375
32375
|
var package_default = {
|
|
32376
32376
|
name: "@hasna/skills",
|
|
32377
32377
|
version,
|
|
@@ -44349,6 +44349,78 @@ function verifiesCodexHookDiscoverySource(binding, source, currentDigest, witnes
|
|
|
44349
44349
|
import { spawnSync as spawnSync5 } from "child_process";
|
|
44350
44350
|
import { accessSync as accessSync2, constants as constants16, existsSync as existsSync6, lstatSync as lstatSync22, realpathSync as realpathSync10, statSync as statSync7 } from "fs";
|
|
44351
44351
|
import { delimiter as delimiter2, isAbsolute as isAbsolute21, join as join27, resolve as resolve20 } from "path";
|
|
44352
|
+
|
|
44353
|
+
// src/lib/hook-diagnostics.ts
|
|
44354
|
+
var CONTEXT_ERROR_CODES = new Set([
|
|
44355
|
+
"SKILLS_API_CREDENTIAL_UNAVAILABLE",
|
|
44356
|
+
"SKILLS_API_UNAVAILABLE",
|
|
44357
|
+
"SKILLS_API_UNAUTHORIZED",
|
|
44358
|
+
"SKILLS_API_FORBIDDEN",
|
|
44359
|
+
"SKILLS_API_RESOURCE_UNAVAILABLE",
|
|
44360
|
+
"SESSION_WRITE_BUSY",
|
|
44361
|
+
"SKILLS_CONTEXT_FAILED",
|
|
44362
|
+
"SKILLS_STORAGE_FULL",
|
|
44363
|
+
"PROFILE_LOCK_MISMATCH",
|
|
44364
|
+
"PROFILE_IDENTITY_MISMATCH",
|
|
44365
|
+
"PROFILE_APPLIED_REPORT_FAILED",
|
|
44366
|
+
"CACHED_AUTHORITY_REQUIRED",
|
|
44367
|
+
"CACHED_PROFILE_EXPIRED",
|
|
44368
|
+
"CACHED_PROFILE_MISSING",
|
|
44369
|
+
"CACHED_BUNDLE_MISSING",
|
|
44370
|
+
"INVALID_CONTEXT_INPUT",
|
|
44371
|
+
"CONTEXT_INPUT_TOO_LARGE",
|
|
44372
|
+
"INVALID_CONTEXT_BUDGET",
|
|
44373
|
+
"INVALID_SELECTION",
|
|
44374
|
+
"INVALID_SELECTION_ALIASES",
|
|
44375
|
+
"INVALID_RECEIPT",
|
|
44376
|
+
"INVALID_CACHE_FILE",
|
|
44377
|
+
"UNSAFE_CACHE_PATH",
|
|
44378
|
+
"RECEIPT_TOO_LARGE",
|
|
44379
|
+
"BUNDLE_DIGEST_MISMATCH",
|
|
44380
|
+
"BUNDLE_IDENTITY_MISMATCH",
|
|
44381
|
+
"BUNDLE_UNAVAILABLE",
|
|
44382
|
+
"BUNDLE_TOO_LARGE",
|
|
44383
|
+
"SKILL_NOT_SELECTED",
|
|
44384
|
+
"INVALID_SESSION",
|
|
44385
|
+
"SESSION_PARENT_NOT_FOUND",
|
|
44386
|
+
"SESSION_PARENT_MISMATCH",
|
|
44387
|
+
"SESSION_NOT_FOUND",
|
|
44388
|
+
"SESSION_GENERATION_CHANGED",
|
|
44389
|
+
"SESSION_GENERATION_EXHAUSTED",
|
|
44390
|
+
"SESSION_PARENT_CHANGED",
|
|
44391
|
+
"SESSION_RECEIPT_CHANGED",
|
|
44392
|
+
"SESSION_RECONCILIATION_REQUIRED",
|
|
44393
|
+
"SESSION_RENEWAL_TIMEOUT",
|
|
44394
|
+
"SESSION_CONTEXT_CONFLICT",
|
|
44395
|
+
"SESSION_RECONCILIATION_INCOMPLETE",
|
|
44396
|
+
"SESSION_WRITE_LOCKED",
|
|
44397
|
+
"SESSION_WRITE_LOCK_CHANGED",
|
|
44398
|
+
"SESSION_LOCK_RECOVERY_INCOMPLETE"
|
|
44399
|
+
]);
|
|
44400
|
+
var HOOK_REFUSAL_REASON_CODES = Object.freeze([
|
|
44401
|
+
...CONTEXT_ERROR_CODES,
|
|
44402
|
+
"SKILLS_HOOK_FAILED",
|
|
44403
|
+
"SKILLS_HOOK_TIMEOUT",
|
|
44404
|
+
"SKILLS_HOOK_INVALID_RESPONSE"
|
|
44405
|
+
]);
|
|
44406
|
+
var OPTIONAL_CONTEXT_FAILURES = new Set([
|
|
44407
|
+
"SKILLS_API_CREDENTIAL_UNAVAILABLE",
|
|
44408
|
+
"SKILLS_API_UNAVAILABLE",
|
|
44409
|
+
"SKILLS_API_UNAUTHORIZED",
|
|
44410
|
+
"SKILLS_API_FORBIDDEN",
|
|
44411
|
+
"SKILLS_API_RESOURCE_UNAVAILABLE",
|
|
44412
|
+
"CACHED_PROFILE_EXPIRED",
|
|
44413
|
+
"CACHED_PROFILE_MISSING",
|
|
44414
|
+
"CACHED_BUNDLE_MISSING",
|
|
44415
|
+
"SESSION_RECONCILIATION_REQUIRED",
|
|
44416
|
+
"SESSION_RENEWAL_TIMEOUT",
|
|
44417
|
+
"SESSION_WRITE_BUSY",
|
|
44418
|
+
"SESSION_PARENT_CHANGED",
|
|
44419
|
+
"SESSION_CONTEXT_CONFLICT",
|
|
44420
|
+
"SKILLS_HOOK_TIMEOUT"
|
|
44421
|
+
]);
|
|
44422
|
+
|
|
44423
|
+
// src/lib/agent-sumi.ts
|
|
44352
44424
|
var SUMI_PATH_SELECTORS = ["SUMI_HOME", "SUMI_CONFIG_DIR", "XDG_DATA_HOME", "XDG_CACHE_HOME", "XDG_CONFIG_HOME", "XDG_STATE_HOME"];
|
|
44353
44425
|
var SUMI_PATH_KEYS = ["data", "cache", "config", "state"];
|
|
44354
44426
|
var SUMI_PATH_OUTPUT_LIMIT = 128 * 1024;
|
|
@@ -44490,24 +44562,33 @@ function sumiConfigPath(home, cwd = process.cwd(), env = process.env) {
|
|
|
44490
44562
|
return join27(sumiConfigDirectory(home, env, cwd), "sumi.json");
|
|
44491
44563
|
}
|
|
44492
44564
|
function renderSumiPlugin(command, profile) {
|
|
44493
|
-
return renderSumiPluginVersion(command, profile,
|
|
44565
|
+
return renderSumiPluginVersion(command, profile, 3);
|
|
44494
44566
|
}
|
|
44495
44567
|
function isManagedSumiPlugin(text2, command, profile) {
|
|
44496
|
-
return text2 === renderSumiPlugin(command, profile) || text2 === renderSumiPluginVersion(command, profile,
|
|
44568
|
+
return text2 === renderSumiPlugin(command, profile) || text2 === renderSumiPluginVersion(command, profile, 2) || text2 === renderSumiPluginVersion(command, profile, 1);
|
|
44497
44569
|
}
|
|
44498
|
-
function renderSumiPluginVersion(command, profile,
|
|
44570
|
+
function renderSumiPluginVersion(command, profile, version2) {
|
|
44571
|
+
const legacy = version2 === 1;
|
|
44499
44572
|
const failure = (message) => legacy ? `new Error(${JSON.stringify(message)})` : "refusal()";
|
|
44500
44573
|
return `// Managed by @hasna/skills. Regenerate with skills hook install --agent sumi.
|
|
44501
44574
|
import { spawn } from "node:child_process";
|
|
44502
44575
|
const command = ${JSON.stringify(command)};
|
|
44503
44576
|
const profile = ${JSON.stringify(profile)};
|
|
44504
|
-
${legacy ? "" : `function refusal() {
|
|
44505
|
-
const error = new Error("Skills verification blocked this request. Review the Sumi Skills hook configuration, then retry.");
|
|
44577
|
+
${legacy ? "" : `function refusal(${version2 === 3 ? "reasonCode" : ""}) {
|
|
44578
|
+
const error = new Error("Skills verification blocked this request. Review the Sumi Skills hook configuration, then retry."${version2 === 3 ? ' + (reasonCode ? " [" + reasonCode + "]" : "")' : ""});
|
|
44506
44579
|
error.name = "SkillsHookRefusal";
|
|
44507
|
-
error.skillsHookRefusal = { version: 1, code: "SKILLS_HOOK_REFUSED" };
|
|
44580
|
+
error.skillsHookRefusal = { version: 1, code: "SKILLS_HOOK_REFUSED"${version2 === 3 ? ", ...(reasonCode ? { reasonCode } : {})" : ""} };
|
|
44508
44581
|
return error;
|
|
44509
44582
|
}
|
|
44510
|
-
`}
|
|
44583
|
+
`}${version2 === 3 ? `const reasonCodes = new Set(${JSON.stringify(HOOK_REFUSAL_REASON_CODES)});
|
|
44584
|
+
function refusedResult(result) {
|
|
44585
|
+
const reason = result.reason ?? result.stopReason ?? result.systemMessage;
|
|
44586
|
+
if (typeof reason !== "string" || reason.length > 16384) return refusal();
|
|
44587
|
+
if (reason.startsWith("NATIVE_SKILL_DRIFT:")) return refusal("NATIVE_SKILL_DRIFT");
|
|
44588
|
+
const code = /^Skills context is unavailable \\[([A-Z_]+)\\]/.exec(reason)?.[1];
|
|
44589
|
+
return refusal(reasonCodes.has(code) ? code : undefined);
|
|
44590
|
+
}
|
|
44591
|
+
` : ""}function context(input) {
|
|
44511
44592
|
return new Promise((resolve, reject) => {
|
|
44512
44593
|
const child = spawn(command, ["hook", "user-prompt", "--agent", "sumi", "--selection-profile", profile], { cwd: input.cwd, stdio: ["pipe", "pipe", "pipe"] });
|
|
44513
44594
|
const chunks = []; let bytes = 0, failure;
|
|
@@ -44522,12 +44603,12 @@ ${legacy ? "" : `function refusal() {
|
|
|
44522
44603
|
try {
|
|
44523
44604
|
const result = JSON.parse(Buffer.concat(chunks).toString("utf8"));
|
|
44524
44605
|
if (!result || typeof result !== "object" || Array.isArray(result)) throw ${failure("Invalid Skills hook response")};
|
|
44525
|
-
if (result.decision === "block" || result.continue === false) throw ${failure("Skills prompt hook refused")};
|
|
44606
|
+
if (result.decision === "block" || result.continue === false) throw ${version2 === 3 ? "refusedResult(result)" : failure("Skills prompt hook refused")};
|
|
44526
44607
|
if (Object.keys(result).length && (!result.hookSpecificOutput || result.hookSpecificOutput.hookEventName !== input.hook_event_name)) throw ${failure("Invalid Skills hook response")};
|
|
44527
44608
|
const text = result.hookSpecificOutput?.additionalContext ?? "";
|
|
44528
44609
|
if (typeof text !== "string") throw ${failure("Invalid Skills prompt context")};
|
|
44529
44610
|
resolve(text);
|
|
44530
|
-
} catch (error) { reject(${legacy ? "error" : "refusal()"}); }
|
|
44611
|
+
} catch (error) { reject(${legacy ? "error" : version2 === 3 ? 'error instanceof Error && error.name === "SkillsHookRefusal" ? error : refusal()' : "refusal()"}); }
|
|
44531
44612
|
});
|
|
44532
44613
|
child.stdin.end(JSON.stringify(input));
|
|
44533
44614
|
});
|
|
@@ -44560,9 +44641,13 @@ export default {
|
|
|
44560
44641
|
sessions.add(event.sessionID);
|
|
44561
44642
|
}));
|
|
44562
44643
|
registrations.push(await ctx.session.hook("context", async event => {
|
|
44563
|
-
|
|
44644
|
+
${version2 === 3 ? ` // Model messages also contain checkpoints, attachments and synthetic
|
|
44645
|
+
// user-role history, with no owner-prompt provenance. Prompt admission
|
|
44646
|
+
// already matched and persisted Skills; restore that verified state only.
|
|
44647
|
+
const prompt = "";
|
|
44648
|
+
` : ` const last = event.messages.findLast(message => message.role === "user");
|
|
44564
44649
|
const prompt = (last?.content ?? []).filter(part => part.type === "text").map(part => part.text).join("\\n");
|
|
44565
|
-
// Recheck on every primary request, including resumed and synthetic
|
|
44650
|
+
`} // Recheck on every primary request, including resumed and synthetic
|
|
44566
44651
|
// sessions. The CLI owns optional failures versus explicit refusals.
|
|
44567
44652
|
const text = await context(await input(event.sessionID, prompt, !sessions.has(event.sessionID)));
|
|
44568
44653
|
if (text) event.system.push({ type: "text", text });
|