← Files TokenXARCHIVED FILE

scripts/lib/hook-io.mjs

1.98 KB · Oct 2, 2026 · 00:29 UTC

↓ Download file

import {
  requirePlainObject,
  validateHookInput,
} from "./contracts.mjs";

const maxInputBytes = 1024 * 1024;
const maxContextBytes = 512;
const contextEvents = new Set(["SessionStart", "UserPromptSubmit"]);

function requireBoundedString(value, label, maxBytes = maxContextBytes) {
  if (typeof value !== "string" || value.length === 0) {
    throw new TypeError(`${label} must be a non-empty string`);
  }
  if (Buffer.byteLength(value, "utf8") > maxBytes) {
    throw new RangeError(`${label} must not exceed ${maxBytes} bytes`);
  }
}

export function parseHookInput(text) {
  if (typeof text !== "string") {
    throw new TypeError("hook input must be a string");
  }
  if (Buffer.byteLength(text, "utf8") > maxInputBytes) {
    throw new RangeError("hook input exceeds 1 MiB");
  }
  let value;
  try {
    value = JSON.parse(text);
  } catch (error) {
    throw new SyntaxError(`invalid hook input JSON: ${error.message}`);
  }
  return validateHookInput(value);
}

export function additionalContextResult(eventName, context) {
  if (!contextEvents.has(eventName)) {
    throw new RangeError(
      `additional context is unsupported for ${String(eventName)}`,
    );
  }
  requireBoundedString(context, "additional context");
  return {
    hookSpecificOutput: {
      hookEventName: eventName,
      additionalContext: context,
    },
  };
}

export function allowToolInputResult(updatedInput) {
  requirePlainObject(updatedInput, "updated Agent input");
  return {
    hookSpecificOutput: {
      hookEventName: "PreToolUse",
      permissionDecision: "allow",
      updatedInput: structuredClone(updatedInput),
    },
  };
}

export function denyToolResult(reason) {
  requireBoundedString(reason, "tool denial reason");
  return {
    hookSpecificOutput: {
      hookEventName: "PreToolUse",
      permissionDecision: "deny",
      permissionDecisionReason: reason,
    },
  };
}

export function serializeHookResult(result) {
  requirePlainObject(result, "hook result");
  return `${JSON.stringify(result)}\n`;
}

SHA-256: 0365bf79af61604e3d11fc9e834e7c9ff3a6b593633abd2f0ca0ba1bbb935ee7