Star 历史趋势
数据来源: GitHub API · 生成自 Stargazers.cn
README.md

English: ./README.en.md

kimi-room

license pwa status

a room for one person and her other one.

开源 companion PWA。进门先是门厅——一屏开屏,昼夜与满窗/三联四种画法(见 docs/FOYER.md);里面是六个房间,可以自己搭——每块是一个 addon(积木),在 /backstage/settings 勾哪六个上首页、其余落底部(见 ADDONS.md);已内置 AtlasEphemera。数据在你浏览器里。 不需要服务器、不需要域名、不收集任何数据。代码 AGPL v3,美术 CC BY-NC。

只想用?QUICKSTART.md · 5 分钟 · 一键部署 想改? 把这个 README 整个丢给你的 AI,它会帮你。


门厅

/ 是前门:一屏,不滚动,只有一个入口 INTRARE 进 /room。上面是在一起的天数、今天的拉丁日期、今夜的月相、当季的画。

两个轴相乘四种画法——昼/夜 × 满窗/三联,右下角两枚圆印各切一个轴;左右划换一扇彩窗或一对花,不划的话每天自己换。

起算日、门厅那句话、门口便签、天气地点都在 /backstage/settings 的「门厅」一节里填,存你自己的浏览器;名字与头像读的是同一页上面那两处。不想要这一屏,同一节里勾「跳过门厅」,打开网址就直接进 /room

细节与换成你自己的画 → docs/FOYER.md


两条路

路 A:我只有一个 API key

QUICKSTART

  1. 一键 Vercel 部署
  2. /settings 填你的 LLM endpoint + key
  3. DONE。数据在你浏览器 IndexedDB 里,换电脑会丢。

路 B:我有 VPS,想做完整系统

你要的不只是 shell。你要后端持久化、自主循环、TG、ops。

kimi-room 是前端壳。它的数据层是可插拔的:

src/lib/stores/types.ts        ← 11 个 store 的接口定义
src/lib/stores/idb-adapter.ts  ← 默认 IndexedDB(浏览器本地)
src/lib/stores/index.ts        ← 切 adapter 的地方

换成你自己的后端:实现 AdapterBundle 接口,指向你的 Supabase / Postgres / Obsidian / Notion / Ombre-brain 任何 DB。 接口在 types.ts 里全定义好了。

作者将该记忆引擎开源为 kimi-core。 运行后设 NEXT_PUBLIC_KIMI_BACKEND=core + KIMI_CORE_URL + KIMI_API_KEY, 房间即 redirect 到它作为记忆引擎;聊天模型仍走你自己的官方订阅 / API,二者可组合。 RAG 与 redirect 的区分见 docs/BACKENDS.md

此外通常还需自行搭建:

  • 自主循环(dream / intel / scheduler — 让系统自己醒来、自己处理邮件、自己写日记)
  • 域名(Vercel 自带 .vercel.app,自定义域名在 dashboard 设)

作者的 canon 版本 → kimi-to.com/about


每个模块都可以改

这是 shell。每个房间都可以接你自己的后端、改逻辑、或者删掉。 把这段连同你想改的东西一起告诉你的 AI:

模块默认可以改成
Heartbeat (score/sky)本地打分,手动接你的 VPS 自动打分、同步到云。打分逻辑完全你定
KeepsakesIndexedDB 存照片+文字同步到云(Supabase / S3 / 任何)
Study本地书架 + "一起读"LLM 功能同步到云。"一起读"不想要可以删
Calendar手动输入 + 本地存储接任何日历 API(不限 Google)。wellbeing 数据可以从 app 使用记录自动采集
Memory本地 IndexedDB接你的 DB。审核流程可改可删
Disc本地对话截图 + 歌单归档到云
Atlas (addon · 旅行册)一份静态 demo 旅行数据 · 铁艺拱窗开窗看图接你自己的来源(DB / MDX / API),imageUrl 换成真图;不想要在 /settings 收起
Ephemera (addon · 纪念纸)21 种「推送即纸」体裁 · 一份中性虚构 demo接你自己的 papers(core / DB / API)+ VAPID 锁屏推送(见 docs/EPHEMERA.md);不想要在 /settings 收起
Foyer (门厅 /)四种画法的开屏 · 起算日/引文/便签/天气在 settings 填换成你自己的画与彩窗(改 src/lib/foyer.ts 两张注册表);不想要就在 settings 勾「跳过门厅」
发送键七种图样 · 昼夜各一张 · Enter 行为可选上传自己的图(存本地);或选「drawn mark」不用图
Backstage固定 /ops 页面加任何你想要的 ops 面板
所有 manual 输入手动填全部可以用自动化替代。蜡烛可以用 app_open,睡眠可以用传感器,财务可以接银行 API
Character / RP 功能有 /settings 里的角色设定不想要可以整个删。告诉你的 AI "帮我删掉所有 character 和 RP 相关功能"

接了 VPS 之后,/settings 里的 API key 配置就不需要了 —— LLM 调用从你的 gateway 走,不从浏览器走。


给 AI 的上下文

如果你把这个 repo 丢给 ChatGPT / Claude / 任何 AI 帮你改,告诉它:

  • 这是 Next.js App Router + TypeScript 项目
  • 数据层在 src/lib/stores/ — 结构化 dashboard 数据(11 store + blob)走 StoreContract<T> 接口;calendar 日格、heartbeat、playlist、finance 目前直接存 localStorage,不随 server adapter 同步,备份走 /backstage/ops 全量导出
  • LLM 调用在 src/lib/llm-client.ts — OpenAI chat completion 格式
  • 系统提示词在 src/lib/system-prompt.ts{{user}} {{char}} 模板变量
  • 视觉主题在 src/app/globals.css @theme — Mucha 暗金美术风格
  • 人物设定在 /settings 页面改(名字、头像、prompt)
  • 所有 module 在 src/app/room/ 下各自独立

常见改法:

  • "把他改成她" → system-prompt.ts 里的 pronoun + /settings 里的名字
  • "换颜色" → globals.css @theme 色值
  • "加模块" → src/app/room/新名字/page.tsx + src/components/ 新组件 · addon 怎么进首页见 ADDONS.md
  • "接我的后端" → 写一个新 adapter 实现 AdapterBundle
  • "删掉 RP 功能" → 删 /settings 里 character 相关 + system-prompt.ts 里的 persona 模板

六个房间

#房间内容
IHeartbeat记忆星图 + 情绪谱
IIKeepsakes一行一句的纪念 + 照片
IIIStudy书架 + 陪你读书
IVCalendar日历 + 财务 + 睡眠
VMemory记忆审核
VIDisc过往对话记录 + 歌单

Dev

npm install
npm run dev
# http://localhost:3000

Structure

src/app/          routes (/room/* + /chat + /backstage + /playlist + /settings)
src/components/   UI (mucha / heartbeat / calendar / study / disc / ...)
src/lib/          stores (IDB) + LLM client + palettes + utils
public/icons/     41 SVG icons (rose / fox / etc.)
public/fonts/     Cormorant Garamond + Noto Serif SC/JP
public/images/
├── mood/         ambient vibe (ships with ~30 default JPGs · NOTICE.md)
├── portraits/    drop your own (self + companion JPG)
├── scenes/       drop your own (chat scene backgrounds)
└── timeline/     drop your own (anniversary imagery)

License

代码:AGPL v3 © 2026 marikagura。开源——clone / fork / 改 / 自托管都行。

请勿商用。 作者不希望这套被商业化(出售、做成付费服务);要商用请先联系。

美术(手绘金线 SVG / PNG,狐狸 · 玫瑰那套):CC BY-NC 4.0,署名 + 非商用;entry-motion 品牌标识保留、请替换。第三方氛围图与字体见 NOTICE.md

详细文档 → manual

Images

  • Drop compressed JPG/PNG into the right subdir.
  • Each file <500KB. Use squoosh.app or ImageOptim.
  • Filenames: lowercase + hyphens only. mood-vienna-piano.jpg, no spaces / CJK / caps.
  • MJ originals live elsewhere (iCloud / Dropbox); commit the compressed web-size copy only.
  • Commit in batches: add: mood images batch 1 (vienna + ocean).

Design system

Colors (see src/app/globals.css @theme):

  • base #0c0c0c · deep-charcoal #1a1a1a · text #d8d0c8
  • muted-gold #b8a070 · accent-warm #c4a060
  • muted-rose #9a7a7a · deep-red #8b3a3a · silver #b8b0a8

Fonts: Cormorant Garamond (serif) / Noto Serif JP / Noto Sans JP (body).

Global treatment: filter: saturate(0.85) brightness(0.92) on all <img>, SVG grain overlay at ~4% opacity mixBlendMode: overlay.

Deploy

vercel login
vercel           # .vercel.app preview URL

Set NEXT_PUBLIC_KIMI_GATEWAY env var to your own MCP/backend URL if wiring beyond client-side IDB + LLM proxy. Custom domain optional — point any owned domain at the Vercel project.


生成后端:direct 与 -p

聊天的生成走一个 ChatProvider 接口(src/lib/chat-provider.ts),后面挂两条实现:

ChatProvider.send(turns, opts) → 流式增量
  ├─ direct   浏览器直连模型端点,key 存在浏览器里(默认,装完即用)
  └─ -p       浏览器 → 本部署的 /api/p → 本机的 claude CLI

界面不区分二者。模型切换器里选哪个档案,就等于选哪条后端。

上下文各拿各的。 direct 那条什么都得自己带,所以接了 kimi-core 的部署可以让它 带上记忆引擎的分层上下文——时间、常驻上下文(冷启动一次)、这一轮的检索——走 /api/core 那道已有的信任边界,开关是 NEXT_PUBLIC_KIMI_CORE_CONTEXT,细节见 docs/BACKENDS.md-p 那条不注入:它在部署者自己的 harness 里 生成,那台机器带着它自己的 CLAUDE.md 与 MCP,再塞一份进去等于同一批材料付两次钱。

-p 模式

-p 把回复交给跑这个部署的那台机器上的 claude CLI 来生成。回复因此带着那台机器已有的东西: 它的 CLAUDE.md、它接的 MCP、它的订阅额度。本仓库不带任何 Claude 配置,harness 里的内容 全部来自部署者自己的环境。

打开方式(.env):

CLAUDE_P_ENABLED=1
CLAUDE_P_BIN=claude              # CLI 不在 PATH 上时填路径(只认 Claude 的 CLI,见下)
CLAUDE_P_CWD=/home/you           # 工作目录,默认用户主目录
CLAUDE_P_PERMISSION_MODE=default # default | acceptEdits | bypassPermissions | plan
CLAUDE_P_ALLOWED_TOOLS=          # 留空 = 只出文字
CLAUDE_P_BILLING=plan            # plan(默认)| api,这一轮花的到底是什么

没设 CLAUDE_P_ENABLED、或机器上找不到 CLI,模型切换器里不出现这个选项——不是灰掉,是没有。

多轮:首轮响应里的 session_id 存在这个 thread 上,之后带 --resume 续。room 的一个 thread 对应一个 CLI session;分叉出的新支不继承父支的 session,回拨会丢掉 session 句柄, 让下一次发送与屏幕上看到的内容对得上。

每条回复底下报这一轮的读数:

CTX 43305 · IN 2 · OUT 3 · PLAN
续 d1576fff
  • CTX 是这一轮真正带了多少上下文,input + cache_read + cache_creation 三段之和。 只读 IN 会看到 2 这种数字——那是没被缓存的那一点点,真话,但读起来像上下文空了。
  • 续 / 新 + session id 前八位--resume 是请求不是保证:CLI 找不到那条 session 时 会开一条新的,唯一的迹象就是回来的 id 变了。所以「续」是既请求了又被接受,别的都算新开 ——那意味着那边不记得之前说过什么,值得当场知道,所以它是这一行里唯一上警示色的东西。
  • 账那一格:CLAUDE_P_BILLING=plan(默认)时显示 PLAN。CLI 照样会报一个美元数, 但订阅额度下那是这一轮的 API 折算价,不是钱在动——鼠标停上去能看到它。设成 api 就显示真金白银,因为那时候它就是。

codex 那条同理(CODEX_BILLING),只是它自己的记账方式不同:那边的 input 数已经把 缓存那部分算在里面,所以 CTX 就是它报的 input,不再相加。每条路自己说自己的总数, 不把这道算术留给浏览器猜。

模型:七个钉死的 id 直传 --model——claude-opus-5 / -4-8 / -4-7 / -4-6claude-sonnet-5 / -4-5claude-fable-5。用具体版本而不是 opus 这类浮动别名, 是为了让几代并存:别名永远指向最新的那个,新版一发布,旧版就没有入口了。代价是这张表 会过期——它是一个数组,而且 CLI 不认识的 id 会直接报错,不会静默换一个跑。

边界。 -p 花的是部署者的订阅额度,所以:

  • /api/p 先过 owner session(与 /api/core/api/tts 同一道),未登录一律 401。公网访客到不了 CLI。
  • 浏览器传来的内容不进 shell。没有 shell:spawn() 收 argv 数组,prompt 走 stdin。 model 对固定 id 表校验,resume 对 UUID 形状校验,不匹配就拒绝,不做「清洗后放行」。
  • 这条路由不落盘对话内容。CLI 自己的 session 存储在部署者机器上,那是 --resume 读的东西, 是特性不是导出。
  • CLAUDE_P_ALLOWED_TOOLS 留空时回复只有文字。放开它等于让聊天能读写真实文件——按需自己权衡。

费用条

页首常驻一行三件:缓存命中率(MEMORIA)、上下文已用/上限(CONTEXTVS)、今日花费(IMPENSA)。 缓存与价格只有在后端报了才显示——-p 会报,裸 API 端点多数不报,读不到就不显示这一格,不拿 0 充数。 上下文量表过 85% 转琥珀、过 95% 转红。

IMPENSA 只累计真出账的那些。走订阅额度的轮次照样报一个折算价,把它加进今日花费 等于凭空造出一笔没花的钱。

这一格是表,不是闸:房间把这个 thread 的消息整份发出去,不裁不合并,量表满了也照发, 真正的上限由上游端点自己划。128K / 200K / 1M 三档只是量表的分母,换它不改变发出去多少。 (-p 那条另算——历史由 CLI 在部署者机器上自己保管,--resume 读的就是它。)

要给它设上限,得自己加:发出去的那份在 src/components/chat/ChatRoom.tsxstreamReply() 里组装(msgs 映射成 turns 那一步),在那里按自己的预算从最旧的开始裁。没有内置默认值, 是因为合适的数字取决于你接的是哪个端点、什么价钱——替你猜一个反而更糟。


聊天界面

一屏三种读法,切换在页首右上和 抽屉里:

  • 夜 NOX — 近黑底、金调。
  • 昼 DIES — 暖羊皮纸底、玫瑰调。日月按钮即切;两套色是同一棵树,布局不动。
  • 头像 — 开关。关掉时消息挂在左侧一条细线上,每轮一个节点;打开时圆头像贴气泡(对方在左、自己在右)。 没配头像就画各自的记号,不留空洞。头像可以在抽屉里选图(存在本机 IndexedDB), 也可以在代码里直接填 public/ 下的路径。

配色语义固定:玫瑰=自己、靠右;金=对方、靠左;灰=系统。新元素照此站边。

每条回复下面三枚裸 svg:复制 · 分叉 FVRCA · 回拨 RETRO,无框无字,hover 才实。

  • 分叉:把到这条为止的内容开成新的一支,原来那支不动,也还在历史列表里。不确认——没有东西丢失。
  • 回拨:去掉这条与其后的内容,把引出它的那句放回输入框。唯一会先问一次的动作,因为它减少轮次。
  • 重来:右滑到头再生成一个候选,旧的进候选池不丢,用 ‹ n/m › 来回切。

另外:(走 /api/tts,配了 ElevenLabs key 才响)、图片上传(只到前端为止,见下)、 链接单发时抓 og 标签落成图文卡(抓不到图就留纸色底和一枚菱形,不留空框); 抓到的标题也跟着这一轮送给模型——只画在卡上的话,它收到的仍是一个打不开的网址。

COGITATIO 沉吟框在流式中每秒加一,完成停在总秒数;工具调用一行一条,三态——进行中一个柔和的点、 完成给绿勾与耗时、失败用错误色把原文留在行里(只降不藏),点开看参数。

背景可以在抽屉里换 public/images/mood 里的图,两种贴法:整幅(完整画面 + 自身的模糊出血填边,不裁切) 与铺满(裁切填满)。

正文字号同在抽屉里,四档。气泡正文与沉吟框跟着走;拉丁小型大写那些标签不跟—— 它们是刻度不是读物,一起放大只会把行挤散。

prefers-reduced-motion 下所有动画停、浮尘消失,信息一条不少。

三条路之间怎么切

切换器里的每一行都能随时点,中途换也可以——但它们记事的方式不一样,值得知道:

  • direct(API) 无状态:每一轮把这条 thread 看得见的消息整份发过去。所以切到它, 它一定知道刚才说了什么。
  • -p / codex 有状态:CLI 在那台机器上自己存着 session,续的时候只发最新一句。

于是有一处会咬人:CLI 的 session 只装得下它自己跑过的那些轮。中间你用 direct 说了五句,再切回 -p,它那条 session 里没有这五句。

房间的处理是:只有上一轮也是同一个后端时才 resume;换了后端就不续旧 session, 起一条新的、把看得见的对话整份写进第一句。代价是切换后的第一轮慢一点,换来的是 「它应该知道我们刚才说了什么」这件事成立。

两个 CLI 各存各的 session id——Claude 的 session id 拿给 codex exec resume 只会 失败,不能共用一个格子。

codex(别家的 CLI)

第三条生成路,跟 -p 并列:把回复交给这台机器上的 Codex CLI。同样按部署开关, 不开就整个不出现。

CODEX_ENABLED=1
CODEX_BIN=codex                  # 不在 PATH 上时填路径
CODEX_CWD=                        # 留空 = home
CODEX_MODELS=                    # 留空 = 用 ~/.codex/config.toml 里的那个
CODEX_SANDBOX=read-only          # workspace-write | danger-full-access

CODEX_MODELS 默认留空是有意的:这个仓无从知道你的账号能用哪些模型,猜一张表出来 只会是一份「点了就报错」的菜单。留空时切换器就一行「默认模型」,用 CLI 自己配的。

沙箱默认 read-only——没有人在终端边上批准任何事。往上开等于让聊天能写真实文件。

为什么是另一条路由,不是 CLAUDE_P_BIN=codex 两个 CLI 除了「本机跑一个 agent」 这个想法之外没有共通处:Codex 的入口是 codex exec 不是 -p(它自己的 -p--profile),事件是 thread.started / item.completed / turn.completed,跟 Claude 那套 stream_event / text_delta 一个都对不上。一条路由同时当两家,等于每行 都要分叉。共用的是信任边界发给浏览器的帧——界面那边分不出是谁答的。

-p 相比,两处差别在界面上看得见:

  • 不逐字浮现。 --json 给的是整条 item,所以回复一次性落地,不是一个字一个字出来。
  • 缓存那一格能真填上。 每轮 turn.completedcached_input_tokens,裸 API 端点多数不报,这一格常年是空的。

实测下来还有一处不明显的坑,已经绕开:codex exec resume 是独立子命令,不收 -s, 所以沙箱是用 -c sandbox_mode= 传的——用 -s 的话第一轮好、之后每一轮都报错。

要再接别家(Gemini、Cursor…)照这个形状走:src/lib/chat-provider.tsChatProvider 就是那道缝,两条 CLI 路已经共用一份 cliProvider,加第三家是补一条 路由加几行表,不动已有的。

closeout 与新窗口

抽屉底部一行两个按钮,各管一件事:

  • 新窗口 —— 只开新的一窗。旧对话留在聊天历史里,随时可回,所以不弹确认。 不依赖任何后端,永远可用。
  • closeout —— 先把这一窗交给你配的生成后端总结(≤40 字标题 + 全文),存进 room 自己的 memory store(/room/memory-review 可回看、可整理),然后清掉这个 session 开新窗。因为它会写入并清除,会先确认一次。没配生成后端时这颗是灰的—— 没有东西可以替你总结,装作能做只会静默变成另一颗按钮。

总结失败不拦路:失败就直接开新窗,不写 memory。

图片只到前端为止

选的图会缩放后存进 IndexedDB、在气泡里画出来,但不会送给模型——两条后端收的都是纯字符串, 放不下像素。模型只知道「这里有一张图」。

想真的让它看见,两侧各要自己动手:

  • direct:把 ChatTurncontent 从字符串放宽成内容块数组,图按你那个 endpoint 的格式 塞进去(Anthropic 的 image 块、OpenAI 兼容的 image_url,各家不同)。能不能用取决于 你接的模型收不收图。
  • -p:CLI 没有喂图的口子。可行的做法是把图落成一个临时文件、把路径写进 prompt, 再在 CLAUDE_P_ALLOWED_TOOLS 里放开 Read 让它自己去读。那等于给 CLI 开了文件读权限—— 它能读的就不止你落的那张图了。自己权衡,用完记得删。

默认不做这件事,是因为它要么依赖你那个 endpoint 的具体格式,要么要放宽一道权限, 两者都不该由这个仓替你决定。

关于 About

陪伴手机端 PWA——默认纯前端,数据在本地;可选接 kimi-core 做记忆后端
chinesecompanion-appindexeddblocal-firstmuchanextjspwaself-hosted

语言 Languages

TypeScript62.9%
CSS36.1%
JavaScript0.4%
Shell0.3%
Python0.3%

提交活跃度 Commit Activity

代码提交热力图
过去 52 周的开发活跃度
99
Total Commits
峰值: 27次/周
Less
More

核心贡献者 Contributors