```ts
/** Cut a chunked stream into frames at a marker, doubled to escape it. */
export function drainFrames(chunks: string[], marker: string): { frames: string[]; pending: string } {
  if (typeof marker !== "string" || marker.length !== 1) {
    throw new Error("marker must be a single character");
  }
  const buffer = chunks.join("");
  let frames: string[] = [];
  let pending = "";
  let i = 0;

  while (i < buffer.length) {
    if (buffer[i] === marker && i + 1 < buffer.length && buffer[i + 1] === marker) {
      // Escaped marker, keep one literal marker and move past the escape
      frames.push(marker);
      i += 2;
    } else if (buffer[i] === marker) {
      // Lone marker, end current frame
      frames.push(pending);
      pending = "";
      i++;
    } else {
      // Regular character, add to pending text
      pending += buffer[i];
      i++;
    }
  }

  // Any remaining characters in pending are part of the last frame or unresolved
  return { frames, pending };
}
```