@hasna/hooks 0.11.7 → 0.12.0

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/bin/serve.js CHANGED
@@ -131,6 +131,7 @@ function getExplicitDbPath(env = process.env) {
131
131
  }
132
132
 
133
133
  // src/lib/registry.ts
134
+ var CLAUDE_TRASH_GUARD_MATCHER = "^(Bash|Monitor|apply_patch|ApplyPatch|functions\\.apply_patch)$";
134
135
  var HOOKS = [
135
136
  {
136
137
  name: "gitguard",
@@ -181,6 +182,8 @@ var HOOKS = [
181
182
  category: "Git Safety",
182
183
  event: "PreToolUse",
183
184
  matcher: "^(Bash|Write|Edit|MultiEdit|NotebookEdit|apply_patch|ApplyPatch|functions\\.apply_patch)$",
185
+ targetMatchers: { claude: "^(Bash|Monitor|Write|Edit|MultiEdit|NotebookEdit|apply_patch|ApplyPatch|functions\\.apply_patch)$" },
186
+ legacyMatchers: ["^(Bash|Write|Edit|MultiEdit|NotebookEdit|apply_patch|ApplyPatch|functions\\.apply_patch)$"],
184
187
  tags: ["workspace", "repos", "structure", "guard", "safety", "orgs", "multi-agent"]
185
188
  },
186
189
  {
@@ -191,6 +194,8 @@ var HOOKS = [
191
194
  category: "Git Safety",
192
195
  event: "PreToolUse",
193
196
  matcher: "^(Bash|apply_patch|ApplyPatch|functions\\.apply_patch)$",
197
+ targetMatchers: { claude: CLAUDE_TRASH_GUARD_MATCHER },
198
+ legacyMatchers: ["^(Bash|apply_patch|ApplyPatch|functions\\.apply_patch)$"],
194
199
  tags: ["rm", "delete", "trash", "recoverable", "guard", "safety", "bash"],
195
200
  rewritesInput: true,
196
201
  timeoutSeconds: 10
@@ -295,6 +300,28 @@ var HOOKS = [
295
300
  matcher: "Bash",
296
301
  tags: ["security", "credentials", "secrets", "detection", "audit"]
297
302
  },
303
+ {
304
+ name: "signed-link-guard",
305
+ displayName: "Signed Link Guard",
306
+ description: "Refuses gh reads that would print composite GitHub content (bodies, comments, reviews, commit messages, check links and details, raw issue/PR/check API objects) where signed action links appear; bounded scalar projections, diffs, lists and writes pass",
307
+ version: "0.1.0",
308
+ category: "Security",
309
+ event: "PreToolUse",
310
+ matcher: "^(Bash|Monitor)$",
311
+ tags: ["github", "gh", "signed-links", "capabilities", "transcript", "guard", "security"],
312
+ timeoutSeconds: 10
313
+ },
314
+ {
315
+ name: "signed-link-output",
316
+ displayName: "Signed Link Output",
317
+ description: "Claude PostToolUse backstop: replaces signed-link URLs in tool output with a redaction marker (updatedToolOutput, where the running Claude Code supports it) and tells the model not to repeat or store the link. It runs after the tool, so it cannot undo display or other persistence",
318
+ version: "0.1.0",
319
+ category: "Security",
320
+ event: "PostToolUse",
321
+ matcher: "^(Bash|WebFetch|mcp__.*)$",
322
+ tags: ["github", "signed-links", "capabilities", "redaction", "transcript", "security"],
323
+ timeoutSeconds: 10
324
+ },
298
325
  {
299
326
  name: "phonenotify",
300
327
  displayName: "Phone Notify",
@@ -676,7 +703,7 @@ function getHook(name) {
676
703
  import { existsSync as existsSync2, mkdirSync, readFileSync, readdirSync, realpathSync, writeFileSync } from "fs";
677
704
  import { basename, dirname, isAbsolute, join as join3, normalize, resolve as resolve2, sep } from "path";
678
705
 
679
- // ../../../../validation/build/apps/hooks/node_modules/zod/v3/external.js
706
+ // ../../node_modules/.bun/zod@3.25.76/node_modules/zod/v3/external.js
680
707
  var exports_external = {};
681
708
  __export(exports_external, {
682
709
  void: () => voidType,
@@ -788,7 +815,7 @@ __export(exports_external, {
788
815
  BRAND: () => BRAND
789
816
  });
790
817
 
791
- // ../../../../validation/build/apps/hooks/node_modules/zod/v3/helpers/util.js
818
+ // ../../node_modules/.bun/zod@3.25.76/node_modules/zod/v3/helpers/util.js
792
819
  var util;
793
820
  (function(util2) {
794
821
  util2.assertEqual = (_) => {};
@@ -919,7 +946,7 @@ var getParsedType = (data) => {
919
946
  }
920
947
  };
921
948
 
922
- // ../../../../validation/build/apps/hooks/node_modules/zod/v3/ZodError.js
949
+ // ../../node_modules/.bun/zod@3.25.76/node_modules/zod/v3/ZodError.js
923
950
  var ZodIssueCode = util.arrayToEnum([
924
951
  "invalid_type",
925
952
  "invalid_literal",
@@ -1038,7 +1065,7 @@ ZodError.create = (issues) => {
1038
1065
  return error;
1039
1066
  };
1040
1067
 
1041
- // ../../../../validation/build/apps/hooks/node_modules/zod/v3/locales/en.js
1068
+ // ../../node_modules/.bun/zod@3.25.76/node_modules/zod/v3/locales/en.js
1042
1069
  var errorMap = (issue, _ctx) => {
1043
1070
  let message;
1044
1071
  switch (issue.code) {
@@ -1141,7 +1168,7 @@ var errorMap = (issue, _ctx) => {
1141
1168
  };
1142
1169
  var en_default = errorMap;
1143
1170
 
1144
- // ../../../../validation/build/apps/hooks/node_modules/zod/v3/errors.js
1171
+ // ../../node_modules/.bun/zod@3.25.76/node_modules/zod/v3/errors.js
1145
1172
  var overrideErrorMap = en_default;
1146
1173
  function setErrorMap(map) {
1147
1174
  overrideErrorMap = map;
@@ -1149,7 +1176,7 @@ function setErrorMap(map) {
1149
1176
  function getErrorMap() {
1150
1177
  return overrideErrorMap;
1151
1178
  }
1152
- // ../../../../validation/build/apps/hooks/node_modules/zod/v3/helpers/parseUtil.js
1179
+ // ../../node_modules/.bun/zod@3.25.76/node_modules/zod/v3/helpers/parseUtil.js
1153
1180
  var makeIssue = (params) => {
1154
1181
  const { data, path, errorMaps, issueData } = params;
1155
1182
  const fullPath = [...path, ...issueData.path || []];
@@ -1255,14 +1282,14 @@ var isAborted = (x) => x.status === "aborted";
1255
1282
  var isDirty = (x) => x.status === "dirty";
1256
1283
  var isValid = (x) => x.status === "valid";
1257
1284
  var isAsync = (x) => typeof Promise !== "undefined" && x instanceof Promise;
1258
- // ../../../../validation/build/apps/hooks/node_modules/zod/v3/helpers/errorUtil.js
1285
+ // ../../node_modules/.bun/zod@3.25.76/node_modules/zod/v3/helpers/errorUtil.js
1259
1286
  var errorUtil;
1260
1287
  (function(errorUtil2) {
1261
1288
  errorUtil2.errToObj = (message) => typeof message === "string" ? { message } : message || {};
1262
1289
  errorUtil2.toString = (message) => typeof message === "string" ? message : message?.message;
1263
1290
  })(errorUtil || (errorUtil = {}));
1264
1291
 
1265
- // ../../../../validation/build/apps/hooks/node_modules/zod/v3/types.js
1292
+ // ../../node_modules/.bun/zod@3.25.76/node_modules/zod/v3/types.js
1266
1293
  class ParseInputLazyPath {
1267
1294
  constructor(parent, value, path, key) {
1268
1295
  this._cachedPath = [];
@@ -5362,7 +5389,7 @@ import { readFileSync as readFileSync5 } from "fs";
5362
5389
  import { dirname as dirname4, join as join8 } from "path";
5363
5390
  import { fileURLToPath as fileURLToPath5 } from "url";
5364
5391
 
5365
- // ../../../../validation/build/apps/hooks/node_modules/@hasna/contracts/dist/client/transport.js
5392
+ // ../../node_modules/.bun/@hasna+contracts@1.2.2+944b86d8024e79fa/node_modules/@hasna/contracts/dist/client/transport.js
5366
5393
  import { spawnSync } from "child_process";
5367
5394
  import { closeSync as closeSync2, fstatSync as fstatSync2, openSync as openSync2, readFileSync as readFileSync22 } from "fs";
5368
5395
  import { O_NOFOLLOW as O_NOFOLLOW2, O_NONBLOCK as O_NONBLOCK2, O_RDONLY as O_RDONLY2 } from "constants";
package/dist/index.js CHANGED
@@ -581,7 +581,7 @@ var init_redact = __esm(() => {
581
581
  ];
582
582
  });
583
583
 
584
- // ../../../../validation/build/apps/hooks/node_modules/zod/v3/helpers/util.js
584
+ // ../../node_modules/.bun/zod@3.25.76/node_modules/zod/v3/helpers/util.js
585
585
  var util, objectUtil, ZodParsedType, getParsedType = (data) => {
586
586
  const t = typeof data;
587
587
  switch (t) {
@@ -712,7 +712,7 @@ var init_util = __esm(() => {
712
712
  ]);
713
713
  });
714
714
 
715
- // ../../../../validation/build/apps/hooks/node_modules/zod/v3/ZodError.js
715
+ // ../../node_modules/.bun/zod@3.25.76/node_modules/zod/v3/ZodError.js
716
716
  var ZodIssueCode, quotelessJson = (obj) => {
717
717
  const json = JSON.stringify(obj, null, 2);
718
718
  return json.replace(/"([^"]+)":/g, "$1:");
@@ -833,7 +833,7 @@ var init_ZodError = __esm(() => {
833
833
  };
834
834
  });
835
835
 
836
- // ../../../../validation/build/apps/hooks/node_modules/zod/v3/locales/en.js
836
+ // ../../node_modules/.bun/zod@3.25.76/node_modules/zod/v3/locales/en.js
837
837
  var errorMap = (issue, _ctx) => {
838
838
  let message;
839
839
  switch (issue.code) {
@@ -940,7 +940,7 @@ var init_en = __esm(() => {
940
940
  en_default = errorMap;
941
941
  });
942
942
 
943
- // ../../../../validation/build/apps/hooks/node_modules/zod/v3/errors.js
943
+ // ../../node_modules/.bun/zod@3.25.76/node_modules/zod/v3/errors.js
944
944
  function setErrorMap(map) {
945
945
  overrideErrorMap = map;
946
946
  }
@@ -953,7 +953,7 @@ var init_errors = __esm(() => {
953
953
  overrideErrorMap = en_default;
954
954
  });
955
955
 
956
- // ../../../../validation/build/apps/hooks/node_modules/zod/v3/helpers/parseUtil.js
956
+ // ../../node_modules/.bun/zod@3.25.76/node_modules/zod/v3/helpers/parseUtil.js
957
957
  function addIssueToContext(ctx, issueData) {
958
958
  const overrideMap = getErrorMap();
959
959
  const issue = makeIssue({
@@ -1058,10 +1058,10 @@ var init_parseUtil = __esm(() => {
1058
1058
  });
1059
1059
  });
1060
1060
 
1061
- // ../../../../validation/build/apps/hooks/node_modules/zod/v3/helpers/typeAliases.js
1061
+ // ../../node_modules/.bun/zod@3.25.76/node_modules/zod/v3/helpers/typeAliases.js
1062
1062
  var init_typeAliases = () => {};
1063
1063
 
1064
- // ../../../../validation/build/apps/hooks/node_modules/zod/v3/helpers/errorUtil.js
1064
+ // ../../node_modules/.bun/zod@3.25.76/node_modules/zod/v3/helpers/errorUtil.js
1065
1065
  var errorUtil;
1066
1066
  var init_errorUtil = __esm(() => {
1067
1067
  (function(errorUtil2) {
@@ -1070,7 +1070,7 @@ var init_errorUtil = __esm(() => {
1070
1070
  })(errorUtil || (errorUtil = {}));
1071
1071
  });
1072
1072
 
1073
- // ../../../../validation/build/apps/hooks/node_modules/zod/v3/types.js
1073
+ // ../../node_modules/.bun/zod@3.25.76/node_modules/zod/v3/types.js
1074
1074
  class ParseInputLazyPath {
1075
1075
  constructor(parent, value, path, key) {
1076
1076
  this._cachedPath = [];
@@ -4421,7 +4421,7 @@ var init_types = __esm(() => {
4421
4421
  NEVER = INVALID;
4422
4422
  });
4423
4423
 
4424
- // ../../../../validation/build/apps/hooks/node_modules/zod/v3/external.js
4424
+ // ../../node_modules/.bun/zod@3.25.76/node_modules/zod/v3/external.js
4425
4425
  var exports_external = {};
4426
4426
  __export(exports_external, {
4427
4427
  void: () => voidType,
@@ -4541,7 +4541,7 @@ var init_external = __esm(() => {
4541
4541
  init_ZodError();
4542
4542
  });
4543
4543
 
4544
- // ../../../../validation/build/apps/hooks/node_modules/zod/index.js
4544
+ // ../../node_modules/.bun/zod@3.25.76/node_modules/zod/index.js
4545
4545
  var init_zod = __esm(() => {
4546
4546
  init_external();
4547
4547
  init_external();
@@ -5101,7 +5101,7 @@ var init_profiles = __esm(() => {
5101
5101
  init_app_home();
5102
5102
  });
5103
5103
 
5104
- // ../../../../validation/build/apps/hooks/node_modules/@hasna/contracts/dist/client/transport.js
5104
+ // ../../node_modules/.bun/@hasna+contracts@1.2.2+944b86d8024e79fa/node_modules/@hasna/contracts/dist/client/transport.js
5105
5105
  import { isIP } from "net";
5106
5106
  import { spawnSync } from "child_process";
5107
5107
  import { closeSync as closeSync22, fstatSync as fstatSync22, openSync as openSync22, readFileSync as readFileSync22 } from "fs";
@@ -6979,6 +6979,10 @@ var HOOK_EVENTS = [
6979
6979
  "UserPromptSubmit",
6980
6980
  "SubagentStart"
6981
6981
  ];
6982
+ var CLAUDE_TRASH_GUARD_MATCHER = "^(Bash|Monitor|apply_patch|ApplyPatch|functions\\.apply_patch)$";
6983
+ function matcherFor(meta, target) {
6984
+ return meta.targetMatchers?.[target] ?? meta.matcher;
6985
+ }
6982
6986
  var CATEGORIES = [
6983
6987
  "Git Safety",
6984
6988
  "Code Quality",
@@ -7041,6 +7045,8 @@ var HOOKS = [
7041
7045
  category: "Git Safety",
7042
7046
  event: "PreToolUse",
7043
7047
  matcher: "^(Bash|Write|Edit|MultiEdit|NotebookEdit|apply_patch|ApplyPatch|functions\\.apply_patch)$",
7048
+ targetMatchers: { claude: "^(Bash|Monitor|Write|Edit|MultiEdit|NotebookEdit|apply_patch|ApplyPatch|functions\\.apply_patch)$" },
7049
+ legacyMatchers: ["^(Bash|Write|Edit|MultiEdit|NotebookEdit|apply_patch|ApplyPatch|functions\\.apply_patch)$"],
7044
7050
  tags: ["workspace", "repos", "structure", "guard", "safety", "orgs", "multi-agent"]
7045
7051
  },
7046
7052
  {
@@ -7051,6 +7057,8 @@ var HOOKS = [
7051
7057
  category: "Git Safety",
7052
7058
  event: "PreToolUse",
7053
7059
  matcher: "^(Bash|apply_patch|ApplyPatch|functions\\.apply_patch)$",
7060
+ targetMatchers: { claude: CLAUDE_TRASH_GUARD_MATCHER },
7061
+ legacyMatchers: ["^(Bash|apply_patch|ApplyPatch|functions\\.apply_patch)$"],
7054
7062
  tags: ["rm", "delete", "trash", "recoverable", "guard", "safety", "bash"],
7055
7063
  rewritesInput: true,
7056
7064
  timeoutSeconds: 10
@@ -7155,6 +7163,28 @@ var HOOKS = [
7155
7163
  matcher: "Bash",
7156
7164
  tags: ["security", "credentials", "secrets", "detection", "audit"]
7157
7165
  },
7166
+ {
7167
+ name: "signed-link-guard",
7168
+ displayName: "Signed Link Guard",
7169
+ description: "Refuses gh reads that would print composite GitHub content (bodies, comments, reviews, commit messages, check links and details, raw issue/PR/check API objects) where signed action links appear; bounded scalar projections, diffs, lists and writes pass",
7170
+ version: "0.1.0",
7171
+ category: "Security",
7172
+ event: "PreToolUse",
7173
+ matcher: "^(Bash|Monitor)$",
7174
+ tags: ["github", "gh", "signed-links", "capabilities", "transcript", "guard", "security"],
7175
+ timeoutSeconds: 10
7176
+ },
7177
+ {
7178
+ name: "signed-link-output",
7179
+ displayName: "Signed Link Output",
7180
+ description: "Claude PostToolUse backstop: replaces signed-link URLs in tool output with a redaction marker (updatedToolOutput, where the running Claude Code supports it) and tells the model not to repeat or store the link. It runs after the tool, so it cannot undo display or other persistence",
7181
+ version: "0.1.0",
7182
+ category: "Security",
7183
+ event: "PostToolUse",
7184
+ matcher: "^(Bash|WebFetch|mcp__.*)$",
7185
+ tags: ["github", "signed-links", "capabilities", "redaction", "transcript", "security"],
7186
+ timeoutSeconds: 10
7187
+ },
7158
7188
  {
7159
7189
  name: "phonenotify",
7160
7190
  displayName: "Phone Notify",
@@ -8106,7 +8136,7 @@ function writeHookSettingsPreimage(path, settings, before) {
8106
8136
  // src/lib/claude-skills-coordination.ts
8107
8137
  import { createHash as createHash6 } from "crypto";
8108
8138
 
8109
- // ../../../../validation/build/apps/hooks/node_modules/@hasna/skills/dist/index.js
8139
+ // ../../node_modules/.bun/@hasna+skills@0.9.17+a00d3064559565bb/node_modules/@hasna/skills/dist/index.js
8110
8140
  import { existsSync as existsSync10 } from "fs";
8111
8141
  import { homedir as homedir5 } from "os";
8112
8142
  import { join as join9, resolve as resolve3 } from "path";
@@ -39789,6 +39819,7 @@ function registerHook(name, scope = "global", target = "claude", profile, mement
39789
39819
  if (uniqueEventKeys.length === 0) {
39790
39820
  throw new Error(`Hook '${name}' has no installable events for target '${target}'`);
39791
39821
  }
39822
+ const matcher = matcherFor(meta3, target);
39792
39823
  const settings = readSettings2(scope, target);
39793
39824
  if (!settings.hooks)
39794
39825
  settings.hooks = {};
@@ -39823,10 +39854,12 @@ function registerHook(name, scope = "global", target = "claude", profile, mement
39823
39854
  }
39824
39855
  }
39825
39856
  if (positions.length) {
39826
- if (positions.length !== uniqueEventKeys.length || uniqueEventKeys.some((event) => positions.filter((position) => position.event === event).length !== 1) || positions.some((position) => (position.entry.matcher ?? "") !== (meta3.matcher ?? ""))) {
39857
+ if (positions.length !== uniqueEventKeys.length || uniqueEventKeys.some((event) => positions.filter((position) => position.event === event).length !== 1) || positions.some((position) => (position.entry.matcher ?? "") !== matcher && !(meta3.legacyMatchers ?? []).includes(position.entry.matcher ?? ""))) {
39827
39858
  throw new Error("Codex hook events, matchers or duplicate registrations require explicit reconciliation; no settings were changed.");
39828
39859
  }
39829
39860
  for (const { entry, index } of positions) {
39861
+ if (matcher)
39862
+ entry.matcher = matcher;
39830
39863
  entry.hooks[index] = { ...entry.hooks[index], type: "command", command: hookCommand };
39831
39864
  if (typeof meta3.timeoutSeconds === "number" && meta3.timeoutSeconds > 0)
39832
39865
  entry.hooks[index].timeout = meta3.timeoutSeconds;
@@ -39846,8 +39879,8 @@ function registerHook(name, scope = "global", target = "claude", profile, mement
39846
39879
  const entry = {
39847
39880
  hooks: [hookEntry]
39848
39881
  };
39849
- if (meta3.matcher) {
39850
- entry.matcher = meta3.matcher;
39882
+ if (matcher) {
39883
+ entry.matcher = matcher;
39851
39884
  }
39852
39885
  settings.hooks[eventKey].push(entry);
39853
39886
  }
@@ -41896,7 +41929,7 @@ async function verifyClaudeSafetyConfiguration(options = {}, dependencies = {})
41896
41929
  need8(!/^(?:hooks run trash-guard|hook-trash-guard)(?:\s|$)/.test(handler.command.trim()), "guard_definition_invalid");
41897
41930
  if (binding?.name !== "trash-guard")
41898
41931
  continue;
41899
- need8(binding.home === home && event === "PreToolUse" && handler.type === "command" && group.matcher === "^(Bash|apply_patch|ApplyPatch|functions\\.apply_patch)$" && handler.timeout === 10 && (handler.async === undefined || handler.async === false) && handler.asyncRewake !== true, "guard_definition_invalid");
41932
+ need8(binding.home === home && event === "PreToolUse" && handler.type === "command" && group.matcher === CLAUDE_TRASH_GUARD_MATCHER && handler.timeout === 10 && (handler.async === undefined || handler.async === false) && handler.asyncRewake !== true, "guard_definition_invalid");
41900
41933
  guards.push({ command: handler.command, path: file2.path });
41901
41934
  }
41902
41935
  }
@@ -7,9 +7,12 @@ export declare class NativeRegistrationError extends Error {
7
7
  export interface NativeRegistrationOptions {
8
8
  target: "codex" | "claude";
9
9
  }
10
+ export interface NativeReadinessRegistrationOptions {
11
+ target: "codex" | "claude" | "sumi";
12
+ }
10
13
  export interface NativeRegistrationPlan {
11
14
  schema: "hasna.hooks.native-safety-registration/v1" | "hasna.hooks.native-readiness-registration/v1";
12
- target: "codex" | "claude";
15
+ target: "codex" | "claude" | "sumi";
13
16
  settingsPath: string;
14
17
  resolvedPath: string;
15
18
  beforeSHA256: string;
@@ -24,6 +27,21 @@ export interface NativeRegistrationPlan {
24
27
  interface Dependencies {
25
28
  verify?: typeof verifyNativeSafetyCommand;
26
29
  }
30
+ /** Sumi observes startup inside its own process through the bundled
31
+ * `@hasna/hooks/native-readiness` SDK, enabled by Sumi's own
32
+ * `experimental.trash_readiness` setting, so there is no startup command to
33
+ * write into a harness settings file. The Hooks-owned Sumi registration is the
34
+ * native guard record `~/.hasna/hooks/native/sumi-trash-guard.json`: it pins a
35
+ * Hooks runtime and worker, and Sumi's startup check requires it (Trash runs
36
+ * `hooks safety verify --target sumi --execution`). Planning re-points that
37
+ * record at this installed package without writing. Applying requires the exact
38
+ * plan digest, keeps the previous record as `<path>.before-<sha256>` and journals
39
+ * the operation. Sumi's own configuration is never read or written here. */
40
+ export interface SumiRegistrationDependencies {
41
+ home?: string;
42
+ command?: string;
43
+ verify?: typeof verifyNativeSafetyCommand;
44
+ }
27
45
  export declare const planNativeSafetyRegistration: (options: NativeRegistrationOptions, dependencies?: Dependencies) => Promise<NativeRegistrationPlan>;
28
46
  export declare const applyNativeSafetyRegistration: (options: NativeRegistrationOptions & {
29
47
  expectedPlanDigest: string;
@@ -38,7 +56,7 @@ export declare const applyNativeSafetyRegistration: (options: NativeRegistration
38
56
  guardVerified: boolean;
39
57
  } | {
40
58
  schema: "hasna.hooks.native-safety-registration/v1" | "hasna.hooks.native-readiness-registration/v1";
41
- target: "codex" | "claude";
59
+ target: "codex" | "claude" | "sumi";
42
60
  settingsPath: string;
43
61
  resolvedPath: string;
44
62
  beforeSHA256: string;
@@ -52,10 +70,11 @@ export declare const applyNativeSafetyRegistration: (options: NativeRegistration
52
70
  ok: boolean;
53
71
  changed: boolean;
54
72
  }>;
55
- export declare const planNativeReadinessRegistration: (options: NativeRegistrationOptions) => Promise<NativeRegistrationPlan>;
56
- export declare const applyNativeReadinessRegistration: (options: NativeRegistrationOptions & {
73
+ /** `sumiDependencies` is a test seam for the Sumi target only. */
74
+ export declare const planNativeReadinessRegistration: (options: NativeReadinessRegistrationOptions, sumiDependencies?: SumiRegistrationDependencies) => Promise<NativeRegistrationPlan>;
75
+ export declare const applyNativeReadinessRegistration: (options: NativeReadinessRegistrationOptions & {
57
76
  expectedPlanDigest: string;
58
- }) => Promise<{
77
+ }, sumiDependencies?: SumiRegistrationDependencies) => Promise<{
59
78
  nativeAdoptionVerified: boolean;
60
79
  readinessRunnerVerified?: boolean | undefined;
61
80
  ok: boolean;
@@ -66,7 +85,31 @@ export declare const applyNativeReadinessRegistration: (options: NativeRegistrat
66
85
  guardVerified: boolean;
67
86
  } | {
68
87
  schema: "hasna.hooks.native-safety-registration/v1" | "hasna.hooks.native-readiness-registration/v1";
69
- target: "codex" | "claude";
88
+ target: "codex" | "claude" | "sumi";
89
+ settingsPath: string;
90
+ resolvedPath: string;
91
+ beforeSHA256: string;
92
+ desiredSHA256: string;
93
+ commandSHA256: string;
94
+ action: "unchanged" | "register";
95
+ planDigest: string;
96
+ guardVerified: boolean;
97
+ readinessRunnerVerified?: true;
98
+ nativeAdoptionVerified: false;
99
+ ok: boolean;
100
+ changed: boolean;
101
+ }> | Promise<{
102
+ nativeAdoptionVerified: boolean;
103
+ backup?: string | undefined;
104
+ ok: boolean;
105
+ changed: boolean;
106
+ operationId: `${string}-${string}-${string}-${string}-${string}`;
107
+ planDigest: string;
108
+ settingsSHA256: string;
109
+ guardVerified: boolean;
110
+ } | {
111
+ schema: "hasna.hooks.native-safety-registration/v1" | "hasna.hooks.native-readiness-registration/v1";
112
+ target: "codex" | "claude" | "sumi";
70
113
  settingsPath: string;
71
114
  resolvedPath: string;
72
115
  beforeSHA256: string;
@@ -21,6 +21,17 @@ export interface HookMeta {
21
21
  * reports it.
22
22
  */
23
23
  rewritesInput?: boolean;
24
+ /**
25
+ * A different matcher for one harness. Claude Code's `Monitor` tool runs a
26
+ * shell command and streams its stdout to the model, so the bundled guards
27
+ * add it on Claude; Codex has no such tool and keeps its verified matcher.
28
+ */
29
+ targetMatchers?: Partial<Record<"claude" | "codex", string>>;
30
+ /**
31
+ * Earlier matchers of an installed native registration that an in-place
32
+ * update may replace with the current one.
33
+ */
34
+ legacyMatchers?: string[];
24
35
  /**
25
36
  * Timeout (seconds) written into the agent's settings entry for this hook.
26
37
  * The harness default is 600s, and a TIMED-OUT HOOK DOES NOT BLOCK: a guard
@@ -28,6 +39,10 @@ export interface HookMeta {
28
39
  */
29
40
  timeoutSeconds?: number;
30
41
  }
42
+ /** The Claude Code matcher of the bundled Trash guard registration (Bash, Monitor and patch tools). */
43
+ export declare const CLAUDE_TRASH_GUARD_MATCHER = "^(Bash|Monitor|apply_patch|ApplyPatch|functions\\.apply_patch)$";
44
+ /** The matcher a hook is registered with for one harness. */
45
+ export declare function matcherFor(meta: HookMeta, target: string): string;
31
46
  export declare const CATEGORIES: readonly ["Git Safety", "Code Quality", "Security", "Notifications", "Context Management", "Workflow Automation", "Environment", "Permissions", "Observability", "Agent Teams"];
32
47
  export type Category = (typeof CATEGORIES)[number];
33
48
  export declare const HOOKS: HookMeta[];
@@ -38,10 +38,14 @@ export type NativeSafetyRecord = {
38
38
  harness: "sumi";
39
39
  command: string;
40
40
  };
41
+ /** The exact bytes every writer stores for a Sumi guard record. Planners use
42
+ * the same bytes to compute a desired digest before any write. */
43
+ export declare function nativeSafetyRecordText(command: string): string;
41
44
  export declare function readNativeSafetyRegistration(home?: string): {
42
45
  record: NativeSafetyRecord;
43
46
  sha256: string;
44
47
  path: string;
48
+ text: string;
45
49
  };
46
50
  /** Bounded, non-leaking explanation for a refused binding: which integrity
47
51
  * property the saved record fails. A binding that a third party could rewrite
@@ -199,11 +199,16 @@ function syncDirectory(path) {
199
199
  closeSync2(fd);
200
200
  }
201
201
  }
202
+ function nativeSafetyRecordText(command) {
203
+ const record = { schema: "hasna.hooks.native-safety.v1", harness: "sumi", command };
204
+ return JSON.stringify(record) + `
205
+ `;
206
+ }
202
207
  function readNativeSafetyRegistration(home = homedir2()) {
203
208
  try {
204
209
  home = canonicalHome(home);
205
210
  const path = join2(home, ".hasna/hooks/native", filename), text = readOwned(path);
206
- return { record: parseRecord(text, home), sha256: hash(text), path };
211
+ return { record: parseRecord(text, home), sha256: hash(text), path, text };
207
212
  } catch (error) {
208
213
  if (error instanceof NativeSafetyError)
209
214
  throw error;
@@ -229,9 +234,7 @@ function nativeSafetyBindingIntegrityReason(home = homedir2()) {
229
234
  }
230
235
  function registerNativeSafety(options = {}) {
231
236
  const home = canonicalHome(options.home ?? homedir2());
232
- const record = { schema: "hasna.hooks.native-safety.v1", harness: "sumi", command: options.command ?? installedNativeSafetyCommand("trash-guard") };
233
- const text = JSON.stringify(record) + `
234
- `;
237
+ const text = nativeSafetyRecordText(options.command ?? installedNativeSafetyCommand("trash-guard"));
235
238
  parseRecord(text, home);
236
239
  for (const dir of [join2(home, ".hasna"), join2(home, ".hasna/hooks"), join2(home, ".hasna/hooks/native")]) {
237
240
  try {
@@ -412,6 +415,7 @@ export {
412
415
  verifyNativeSafetyCommand,
413
416
  registerNativeSafety,
414
417
  readNativeSafetyRegistration,
418
+ nativeSafetyRecordText,
415
419
  nativeSafetyBindingIntegrityReason,
416
420
  evaluateNativeSafetyForExecution,
417
421
  evaluateNativeSafety,
package/dist/sdk/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // @bun
2
- // ../../../../validation/build/apps/hooks/node_modules/@hasna/contracts/dist/client/transport.js
2
+ // ../../node_modules/.bun/@hasna+contracts@1.2.2+944b86d8024e79fa/node_modules/@hasna/contracts/dist/client/transport.js
3
3
  import { isIP } from "net";
4
4
  import { spawnSync } from "child_process";
5
5
  import { closeSync as closeSync2, fstatSync as fstatSync2, openSync as openSync2, readFileSync as readFileSync2 } from "fs";