```ts
/**
 * Returns the length in days of the longest stretch of consecutive days that starts on a kept day,
 * ends on a kept day, and holds at most grace unkept days in between.
 * @param counts - Array of daily step counts.
 * @param goal - The step count goal to consider a day as "kept".
 * @param grace - The maximum number of unkept days allowed in between kept days.
 * @returns Length of the longest valid stretch in days.
 */
export function graceWindow(counts: number[], goal: number, grace: number): number {
  if (goal <= 0) {
    throw new Error("Goal must be a positive number.");
  }
  if (grace < 0) {
    throw new Error("Grace must be a non-negative number.");
  }

  const isKeptDay = (count: number): boolean => count >= goal;
  let maxStretch = 0;
  let currentStretch = 0;
  let unkeptDays = 0;

  for (let i = 0; i < counts.length; i++) {
    if (isKeptDay(counts[i])) {
      currentStretch++;
      unkeptDays = 0;
    } else {
      unkeptDays++;
      if (unkeptDays <= grace) {
        currentStretch++;
      } else {
        maxStretch = Math.max(maxStretch, currentStretch);
        currentStretch = 0;
        while (i < counts.length && !isKeptDay(counts[i])) {
          i++;
        }
        if (i < counts.length) {
          currentStretch = 1;
          unkeptDays = 0;
        }
      }
    }
  }

  return Math.max(maxStretch, currentStretch);
}
```