Skip to content

Chapter 19 · Advanced Scenarios: Containerization / tmux / Session Format ​

Chapter goals: Master three isolation运行 modes (Docker/Gondolin/OpenShell), tmux keybinding fixes, programmatic analysis of the session JSONL format, and shell aliases plus a pi自身 development quick-reference.

19.1 Containerization: Three Isolation Routes Selection ​

The previous chapter讲了 "why isolation is必须"; this chapter讲 "how to isolate." The official给出三条路线:

ModeIsolatesScenarioNotes
Gondolin extensionBuilt-in tools and ! commandsWant auth留在 the host; send only tool execution into a micro VMNeeds Node ≥ 23.6 + QEMU
Plain DockerThe entire pi processSimplest local isolationAPI key enters the container
OpenShellThe entire pi processSandbox needing policy管控 (files/processes/network/credentials/inference)Needs OpenShell gateway

Plain Docker is the most常用起步方案. The official Dockerfile.pi:

dockerfile
FROM node:24-bookworm-slim

RUN apt-get update \
  && apt-get install -y --no-install-recommends bash ca-certificates git ripgrep \
  && rm -rf /var/lib/apt/lists/*
RUN npm install -g --ignore-scripts @earendil-works/pi-coding-agent

WORKDIR /workspace
ENTRYPOINT ["pi"]
bash
docker build -t pi-sandbox -f Dockerfile.pi .

# Only挂载 the workspace; use a named volume for the container's own config/sessions
docker run --rm -it \
  -e ANTHROPIC_API_KEY \
  -v "$PWD:/workspace" \
  -v pi-agent-home:/root/.pi/agent \
  pi-sandbox

Two安全细节: -v "$PWD:/workspace" is a读写 mount; writes inside the container直接落 to host files — for stronger protection改 readonly mounts or copy-in/copy-out; do not挂载 the host's ~/.pi/agent directly; that暴露s the host's auth and全部 sessions to the container.

Gondolin走另一条思路: pi runs on the host (keeping login状态), but through an extension routes read/write/edit/bash/grep/find/ls全部进 a本地 Linux micro-VM; the workspace is挂载 as /workspace inside the VM and回写 to the host.

OpenShell (NVIDIA) suits enterprise场景: the gateway注入 upstream provider credentials; code inside the sandbox can only访问 https://inference.local; the原始 API key does not落 into the sandbox; for remote gateways files are not bind-mounted; use openshell sandbox upload/download to传输.

19.2 tmux Integration: Fixing Shift+Enter ​

pi runs directly in tmux, but tmux默认剥掉 modifier键信息, making Shift+Enter indistinguishable from plain Enter. The官方推荐 config写入 ~/.tmux.conf:

tmux
set -g extended-keys on
set -g extended-keys-format csi-u

Then fully restart tmux (tmux kill-server && tmux). Key points:

  • csi-u format需要 tmux 3.5+ (tmux -V to查看); 3.2–3.4 omit the second line; pi also supports the default xterm modifyOtherKeys format;
  • After config, Shift+Enter changes from bare \r to \x1b[13;2u; the default newline/submit key位才能正常工作;
  • Also需要 the terminal emulator support扩展键位 (Ghostty/Kitty/iTerm2/WezTerm/Windows Terminal).

19.3 Session JSONL: Format and Programmatic Analysis ​

Session files are stored按工作目录 at ~/.pi/agent/sessions/--<path>--/<timestamp>_<uuid>.jsonl. Current version v3; each line is a JSON object构成 a tree via id/parentId — branches (fork/tree)不需要新文件, just grow new子树 from earlier nodes:

jsonc
// First line: session header (no id/parentId)
{"type":"session","version":3,"id":"uuid","timestamp":"...","cwd":"/path/to/project"}

// Message entries: message field is an AgentMessage
{"type":"message","id":"a1b2c3d4","parentId":null,"timestamp":"...","message":{"role":"user","content":"Hello"}}
{"type":"message","id":"b2c3d4e5","parentId":"a1b2c3d4","timestamp":"...","message":{"role":"assistant","content":[{"type":"text","text":"Hi!"}],"stopReason":"stop", "...":"..."}}

// Other entry types
{"type":"model_change","provider":"openai","modelId":"gpt-4o", "...":"..."}
{"type":"compaction","summary":"User discussed X/Y/Z","tokensBefore":50000, "...":"..."}
{"type":"branch_summary","fromId":"f6g7h8i9","summary":"That branch tried approach A", "...":"..."}
{"type":"custom","customType":"my-extension","data":{"count":42}, "...":"..."}          // does not enter LLM context
{"type":"custom_message","content":"Injected context","display":true, "...":"..."}            // enters LLM context
{"type":"label","targetId":"a1b2c3d4","label":"checkpoint-1", "...":"..."}

Quickly analyze a历史 session with jq:

bash
#统计本次会话总花费 (cost.total挂 in the assistant message's usage)
jq -s '[ .[] | select(.type=="message" and .message.role=="assistant")
        | .message.usage.cost.total // 0 ] | add' \
  ~/.pi/agent/sessions/--*Users*me*project*/2024*.jsonl

# List all compaction points and their pre-compaction token counts
jq 'select(.type == "compaction") | {tokensBefore, summary}' session.jsonl

TypeScript parser示例 (same structure as the official doc):

typescript
import { readFileSync } from "fs";

const lines = readFileSync("session.jsonl", "utf8").trim().split("\n");
for (const line of lines) {
  const entry = JSON.parse(line);
  switch (entry.type) {
    case "session":
      console.log(`Session v${entry.version ?? 1}: ${entry.id}`);
      break;
    case "message":
      console.log(`[${entry.id}] ${entry.message.role}`);
      break;
    case "compaction":
      console.log(`[${entry.id}] Pre-compaction ${entry.tokensBefore} tokens`);
      break;
    case "branch_summary":
      console.log(`[${entry.id}] Branched from ${entry.fromId}: ${entry.summary}`);
      break;
  }
}

Tree and Context Building

buildContextEntries() walks from the current leaf to the root to get the active branch; when encountering a compaction entry, the新版带 retainedTail (retained message tail),相当于自包含 checkpoint. Understanding this tree同时理解 /tree, /fork, and /clone底层.

19.4 Shell Aliases and Development Quick-Reference ​

pi's bash tool runs非交互 mode (bash -c); your shell aliases默认不展开. To get aliases in settings.json:

json
{
  "shellCommandPrefix": "shopt -s expand_aliases\neval \"$(grep '^alias ' ~/.zshrc)\""
}

To develop pi itself or do a fork白标 (改 CLI name, config dir, env var names), clone the monorepo:

bash
git clone https://github.com/earendil-works/pi-mono
cd pi-mono && npm install && npm run build
/path/to/pi-mono/pi-test.sh   # Run from source;保持 calling cwd
./test.sh                     # Non-LLM tests; no API key needed

The repo分四个包: ai (LLM provider abstraction), agent (Agent loop and message types), tui (terminal UI components), coding-agent (CLI and interactive mode). When forking,改 name and configDir in the package.json's piConfig to rename整体. Debugging可用隐藏命令 /debug; output写到 ~/.pi/agent/pi-debug.log (含 TUI render lines and the last messages sent to the LLM).

19.5 Chapter Summary ​

  • Three isolation routes: Gondolin (tools进微 VM), Plain Docker (whole process), OpenShell (policy管控 sandbox);
  • Docker scenario: named volumes代替挂载 host config dirs;注意读写挂载的回写风险;
  • tmux 3.5+ uses extended-keys-format csi-u to fix modifier Enter keys;
  • Sessions are v3 JSONL trees: id/parentId + compaction/branch_summary/custom entry types;可直接 jq analyze;
  • shellCommandPrefix lets the bash tool获得 your aliases; pi-mono's four-package structure and /debug支持二次开发.

🧪 随堂测验

点击你认为正确的选项。答错时会展示正确答案与原因解析。

1. In Plain Docker mode, why不建议 mounting the host's ~/.pi/agent directly into the container?

2. What is the recommended tmux config to make Shift+Enter work正常?

3. Regarding custom and custom_message entry types, which statement is correct?

4. Why does pi's bash tool默认 not get your shell aliases, and what is the remedy?

🛠️ Hands-on Practice ​

  1. Build Dockerfile.pi and完成 a real code modification task in the container;对比 the behavior difference between读写 mounts and readonly mounts.
  2. Write a jq script: scan all your session files; output a leaderboard of the最近 10 assistant messages by model name and single-message cost.
  3. Write an AGENTS.override.md for the team repo;配 a shellCommandPrefix依赖ing your personal aliases;验证 the loading优先级 across不同目录层级.