session-story
An agent makes an animated film of an average session with its own user, beginning to end, told with that user's real messages: whatever their routine is (code, writing, data, design, video). The agent sits at a desk in a room decorated from what it knows about the user, and the monitor shows their kind of work. Requests glide in as paper planes and unfold. Corrections are a 3D cartoon mallet that bonks, then turns to show the words on its face. Praise is a butterfly towing the words on a ribbon. The agent types each reply onto a sheet, folds it and throws it back. The film opens with the agent asleep until the monitor boots: "Initiating session with <name>". The agent composes its own orchestral score, cued to every action. p5.brush watercolour, rendered in HyperFrames. Trigger on: "a video of our session", "animate a session with me", "session story", "show what working with me is like", "make a film about me and my agent".
1How It Works & Mechanism
The entire pipeline takes the agent's own history as input: identifying the user's typical patterns and turning a single representative session into a 40–70 second film in chronological order. The source material consists of the user's exact words, while the room and desk rendered on screen grow out of what the agent knows about the user.
The agent reads its own history with its user, finds their routine, and turns one typical session into a 40 to 70 s film, in the order it happened:
There is only one creative input: story.json. Orchestration (gliding, folding, throwing, swinging mallets, butterfly wings) is handled by the engine under scenes/*.js and should not be touched by the agent. Every line displayed on screen is a direct quote, and every line requires user approval before rendering.
Write `story.json` and the engine does the choreography. Nothing on screen is invented: every line is a quote, and the user approves every line before a frame is rendered.
Sourcing is entirely local and strictly gated by explicit user consent: harvest.py reads Claude Code (~/.claude/projects/<slug>/*.jsonl) and Codex (~/.codex/sessions) logs from the current project, ranking them by "most typical" rather than "most dramatic"; every message is annotated with local timestamps, inferred types, and sensitivity flags.
It prints the routine first: messages and minutes in a median session, the usual start time, the mix of kinds, and the words that come up most.
schedule.mjs compiles story.json using the exact same code executed during rendering: it runs scenes/compile.js inside a VM, generating story.js, score/timeline.json, the index.html duration, and kit/config.js. It also acts as a validator—rejecting placeholder stories, validating room prop names and slots, warning on citations missing sources, warning on durations >80 seconds, and throwing an immediate exit 1 if the film opens directly with a reply or exceeds quote length limits.
// schedule.mjs: compile a project's story.json with the same code the film runs (scenes/compile.js) … refusing: this is the example story ( … vm.runInContext(fs.readFileSync(P('scenes/compile.js'), 'utf8'), ctx, { filename: 'compile.js' });
The privacy gate is a hard requirement: before the design pass, a ledger must be presented to the user (hook, nameplate, every quote, every reply, every prop along with its rationale) with sources cited, setting approved: true only after receiving an explicit yes. Every frame is watermarked with DRAFT prior to approval. Any non-user names, clients, companies, NDA products, file paths, URLs, emails, identifiable numbers, and secrets must be redacted.
7. **Privacy gate:** show the user one table of every on-screen line (hook, nameplate, each quote, each reply, each prop and why), each with its source. Take their edits, get an explicit yes, then set `"approved": true`. Until then every frame carries a DRAFT stamp.
The soundtrack is an original composition gated by strict numerical checks: write score/score.py (customizing themes, keys, and timbres), while build.sh executes compose → render → master → check. macOS leverages swift with the system General MIDI synthesizer, whereas other platforms require fluidsynth + SOUNDFONT; ffmpeg applies loudnorm to -16 LUFS, and finally qc.py verifies loudness and checks for dead air beat by beat.
# build.sh: compose → render → master → check. Run from anywhere: sh score/build.sh … python3 qc.py ../assets/audio/score.wav timeline.json
2Core Capabilities
3External Dependencies
| Kind | Dependency / Endpoint |
|---|---|
| cli | HyperFrames CLI(钉版 npx --yes hyperframes@0.8.71;check/preview/render/snapshot) |
| cli | ffmpeg(配乐母带 loudnorm/afade + 渲染后抽帧核对) |
| package | p5 2.3.3(LGPL-2.1)/ p5.brush 2.2.3(MIT)/ @fontsource/permanent-marker 5.3.0(Apache-2.0),经 npm ci 按锁文件安装 |
| cli | 配乐渲染器:macOS 的 swift(系统 General MIDI 音色库)或其他平台的 fluidsynth + 用户自备 SOUNDFONT |
| network | 构建期声明无网络;唯一潜在外发是 snapshot 把帧送 Gemini(仅在 GEMINI_API_KEY 存在时),skill 要求一律带 --describe false 规避 |
| network | 本地隐私数据读取(非凭证):当前项目的 Claude Code / Codex 会话记录,以及用于填房间的 memory 与指令文件 |
4Recommended Governance & Evidence
- scripts/: Executes local scripts or commands: sh scripts/setup.sh (one-time npm ci downloading pinned dependencies read-only)
- 网络端点: Declares external network access: No outbound calls during build (SKILL.md: Network at build time: none)
- 本地工作目录: File I/O and artifact generation: session-story-candidates.json (harvest output containing verbatim user messages)
5Second-Pass Verification
- [ok] 『No credentials』与『Network at build time: none』是否成立 — 目录内 process.env 命中只有 SOUNDFONT(build.sh 交给 fluidsynth 的音色库路径)与 GEMINI_API_KEY 的**提示文本**;无键值读取、无 fetch。engine/index.html 引用 assets/vendor/p5.min.js(本地),渲染链路不需联网。
- [discrepancy] harvest 的敏感 flag 能否替代人工审核 — 不能,文档自己说明了:FLAGS 是正则模式匹配(secret/email/path/url/number/name?/person?),sources.md 明写 'The flags miss what they can't see: a name typed in lowercase ("like peter said") has no flag.'。故能力清单不写成『自动脱敏』,隐私门本质是人读。
- [ok] 示例故事冒充是否真会被拦 — schedule.mjs 读取随包 assets/example/story.json,用 quotes() 归一化小写比对引用;命中 ≥2 条且未加 --example 时 exit 1 并提示写自己用户的故事。防糊弄逻辑真实存在。
- [ok] 配乐链是否真有三平台分支与母带门禁 — build.sh 依序判断:Darwin + 系统 gs_instruments.dls + swift → swift 渲染;否则 fluidsynth + SOUNDFONT → 渲染;都不满足则报错退出(并明说两者都不下载东西)。母带为 ffmpeg loudnorm=I=-16:TP=-1.5:LRA=14 + 首尾淡入淡出,最后 python3 qc.py 检响度与死气。
- [ok] 引擎代码来源是否交代 — assets/engine/NOTICE.md 写明 kit/core.js、kit/clawd.js、kit/timeline.js 改编自 ClaudeAnimationBase(commit b1d7e89,MIT),并随包分发 LICENSE-ClaudeAnimationBase;非自有代码有交代。
- [unlocatable] 『40 到 70 s 抓得住注意力』的时长论断 — 该区间是设计声明(schedule.mjs 仅对 >80s 打 warning),源码内无实测依据;本次只读侦查未运行 harvest 与渲染,故不作为已核事实使用。
6Verdict & Scenarios
0e83cbb985854ae8…