Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/cad0p/pi-napkin/llms.txt

Use this file to discover all available pages before exploring further.

When a background distill completes a squash-merge that touches files the parent session has also written, the agent needs to know. Without this signal, the agent might continue editing a note whose contents have just been replaced or merged by the distill subprocess — producing a conflict at the next distill or silently overwriting the distilled content. pi-napkin solves this by injecting an overlap notice directly into the conversation at the moment the distill lands.

The Overlap Notice

When an overlap is detected, the agent sees:
⚠️ Background napkin distill is editing files you've also touched: notes/foo.md. Recent writes to these files may be overwritten or merged automatically at distill completion; consider re-reading before further edits.
The notice names the specific files that overlap, so the agent can decide whether to re-read them, wait for any remaining distills to complete, or proceed with awareness that its writes may be merged automatically.

How It Works

Trigger — the notice fires per distill completion, when the wrapper’s squash-merge has landed and the worktree has been removed. This is the moment files actually change in the parent’s view. The notice does not fire on every agent turn, which would cause unnecessary cache-busting and noise. Channel — the notice is posted via appendCustomMessageEntry with customType: "napkin-distill-overlap". This means it lives in session history, so distill subprocesses that fork the parent session inherit it cleanly. It is also displayed in the TUI so you see it alongside the agent. Cache parity — custom messages land at the end of the message array, leaving the prompt prefix byte-identical between turns. The notice becomes a one-time cache write rather than the recurring cache-bust that the previous per-turn appendSystemPrompt mechanism produced. Frequency — bounded by the per-distill-completion trigger: typically around 5–12 messages per day for an active session, and only when actual file overlap exists. Sessions with no overlapping edits produce no notices at all. Cursor — each completion only considers session entries added since the previous completion. On a fresh session the cursor starts at ctx.sessionManager.getEntries().length (which is 0 for a new session). On a resumed session it starts at the pre-resume entry count, so resuming a session does not surface stale notices for files written in earlier pi processes whose distills have already landed on main.

File Matching Algorithm

The overlap detector matches files in three layers, applied in order:
  1. Exact equality — the canonical case. notes/foo.md matches notes/foo.md.
  2. Symmetric suffix — tolerates absolute session paths against worktree-relative distill paths. /home/user/.napkin/notes/foo.md matches notes/foo.md regardless of which side is absolute and which is relative. Specifically, a pair matches if d.endsWith(/s)ors.endsWith(/{s}`)` or `s.endsWith(`/).
  3. Basename equality — last-resort heuristic for paths that don’t share a suffix. path/to/README.md matches other/README.md.
    The basename fallback has a known false-positive: two unrelated files with the same name in different subtrees (e.g. two separate README.md files) will match. This is accepted because the overlap notice is purely advisory — it never modifies files, only adds a message to the conversation. The trade-off is documented in the overlap-injection tests.

What Triggers an Overlap

The session-touched-files detector tracks write, edit, and bash-redirection-style writes the parent session’s agent has made during the current session slice. It reimplements pi’s internal extractFileOpsFromMessage (which is not exported from pi). A companion version-check test pins the upstream utility so a rename or removal in pi surfaces as an explicit test failure rather than a silent regression.

napkin_distill_status Tool

The agent can proactively query napkin_distill_status before making vault edits to check whether a background distill is currently in flight. This is useful when the agent wants to wait for an in-progress distill to complete before writing to files that might be affected.
{
  "active": [
    {
      "pid": 12345,
      "branch": "distill/abc123-1715198400",
      "elapsedSeconds": 45,
      "session": "session-abc.jsonl",
      "alive": true,
      "startedAt": "2024-05-08T20:00:00.000Z",
      "startSha": "a1b2c3d4e5f6a1b2"
    }
  ],
  "unmerged": []
}
active lists distill worktrees currently registered in the vault’s git worktree list. alive reflects a process.kill(pid, 0) check at read time — a freshly crashed wrapper appears with alive: false. startedAt is the ISO-8601 timestamp from the worktree’s meta.json. startSha is the vault HEAD commit SHA at the moment the worktree was created, used by the overlap detector to enumerate exactly which files the distill affected post-squash. unmerged lists distill/* branches that exist in the repo but have no live worktree, typically leftovers from a previous crashed distill. The slash command equivalent is /distill-status, which formats the same data for human reading.
Overlap notices are non-destructive. They are advisory messages that appear in conversation history and do not modify any files. The agent can choose to re-read the affected files, wait for the distill to complete, or proceed with the understanding that its writes to those files may be merged automatically at the next distill’s squash step.

Build docs developers (and LLMs) love