Commit Graph

676 Commits

Author SHA1 Message Date
fanghanjun 0cd9df7de6 fix(studio): 草稿会话(draft session)不在侧边栏展示,直到发出第一条消息
之前点"+ 新建会话"会立刻在侧边栏多出一条空条目,体验突兀且和"新建书籍"
草稿态(侧边栏不显示草稿节点)行为不一致。

- createDraftSession 不再把 sessionId 追加进 sessionIdsByBook,只放到
  sessions map + activeSessionId(让 ChatPage 显示空对话区)
- sendMessage 的 draft 分支在 POST /sessions 落盘成功那一刻才把 sessionId
  写进 sessionIdsByBook,侧边栏那时才出现此会话(并带上 title)

副作用:用户点新建会话但没聊就切走/关窗,draft 彻底消失(内存都释放),
不会在磁盘和侧边栏留下空白痕迹——与"新建书籍"草稿一致。

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 02:59:18 -07:00
fanghanjun 95ad429646 chore(studio): 清理 server.test.ts 里 updateSessionTitle 的 mock 残留
updateSessionTitle 已在前面的 commit 里随 LLM 生成 title 逻辑一并删除,但
server.test.ts 里还留着 updateSessionTitleMock 的定义、mock 注入、mockReset
和 mockImplementation。这些 mock 没有任何 assertion 引用,所以测试仍然全过,
但属于死代码,删掉避免以后读代码的人困惑。

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 02:50:32 -07:00
fanghanjun 86a85d3175 chore: 把 docs/ 整个排除在 git 外,不再跟踪 spec 和 plan 文件
之前 .gitignore 把 docs/superpowers/ 作为白名单例外跟踪。实际上这些 spec
和 plan 是中间产物,随迭代快速变化且数量多,放在代码仓库里会让每个 PR
都夹带大量文档变更、拖慢 review。

改为整个 docs/ 目录一律不进 git:文件保留在磁盘本地工作,需要留档的内容
通过其它渠道沉淀。

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 02:44:20 -07:00
fanghanjun 338e860d13 docs: add CLAUDE.md + superpowers specs & plans
- CLAUDE.md:项目指南(常用命令、设计文档索引、工作规范、pi-agent 工具设计
  规范、关键路径),供 Claude Code 在会话开头自动加载
- docs/superpowers/specs/:最近几轮 brainstorming 的设计文档
  - 2026-04-13 book-create-redesign / chat-page-redesign / tool-calling-book-create
  - 2026-04-15 sub-agent-params-alignment
  - 2026-04-16 session-management-design
  - 2026-04-17 server-split-design
- docs/superpowers/plans/:对应的实施计划
  - 2026-04-13 ai-elements-integration / book-create-implementation-plan /
    phase-1b-pi-ai-provider / phase-1c-pi-agent-interaction
  - 2026-04-15 sub-agent-params-alignment-plan
  - 2026-04-16 session-management
- agent-context-management-plan.md 在 master 已有历史,此次附上对应的 spec

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 02:29:51 -07:00
fanghanjun 66d94c460e refactor(studio): 把 SSE 事件 listener 从 message action 拆出到 stream-events.ts
之前 sendMessage 里所有 SSE 事件监听(thinking:start/delta/end、draft:delta、
tool:start/end、log、llm:progress)内联在一个 ~400 行的函数里。拆出到
stream-events.ts 的 attachSessionStreamListeners,保留 session id 作用域,
让 message action.ts 只关心消息和 session 状态流转。

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 02:29:42 -07:00
fanghanjun b61d6c677f chore: ignore 用户运行时数据和密钥文件
- inkos.json 包含真实 apiKey,不应进 git
- .inkos/ 放运行时会话文件和 secrets.json
- books/ 是用户创作的书籍内容
- prompt/ 是跨项目共用的提示词模板(FeelFish 资产),非 InkOS 代码

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 02:29:35 -07:00
fanghanjun e4663bcbd8 style(studio): 精简 sidebar 视觉 + rename dialog 紧凑化
Sidebar 书/会话树:
- 默认全部折叠,只有当前 activePage 对应的书会在点击时展开;展开/折叠
  不再触发 /sessions 请求(靠 sessionIdsByBook 缓存,新展开书没加载过
  才拉一次)
- 书名行去掉背景高亮,改 FolderOpen 图标 + 纯文本,ChevronRight 只作
  轻量折叠指示
- 会话行去掉左侧圆点和右侧 ● 选中符号,改为纯文本 + 右对齐相对时间
  (刚刚 / 3 小时 / 6 天 / 2 个月);流式中显示 Loader2 替代时间
- 首条消息展示直接读 title 字段(后端写入),只在极短时间窗内有 title
  未同步时 fallback 到本地 messages 里第一条用户消息

Rename dialog:
- 去掉"手动标题会覆盖自动生成标题,后续不再被 AI 改写"的描述(AI 生成
  逻辑已删,描述也过时)
- 标题改 sans-serif 小字,整体尺寸收窄到 360px,内边距和间距收紧
- 输入框 focus 边框改柔和色,不再红色高亮
- 取消按钮改文字链接风格,保存按钮小而收敛

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 02:27:37 -07:00
fanghanjun 70e440b2f1 feat(studio): 新建会话延迟到第一条消息才持久化(draft session)
用户体验上"点新建会话立即出现",但磁盘层面不立即产生 session 文件:
- SessionRuntime 新增 isDraft 字段,标记仅存在前端未落盘的草稿会话
- 新增 createDraftSession action:客户端生成 sessionId(与后端同格式,
  timestamp-random),在 store 里创建 runtime 但不发 POST /sessions
- sendMessage 检测 isDraft:发消息前先 POST /sessions { sessionId, bookId }
  用同一 id 把 session 持久化,成功后把 isDraft 翻成 false 再发 /agent
- deleteSession 跳过 draft(磁盘上还没文件,省一次 404)
- loadSessionDetail 跳过 draft(远端还不存在)

这样用户点新建会话但没聊就切走的情况不会在磁盘产生孤儿文件;同时因为
sessionId 从一开始就固定,React key 和 URL 不用切换,切换顺滑。

同时:
- use-sse.ts 新增 session:title 事件类型,让前端订阅后端"第一条消息写入
  title"的广播,实时刷新侧边栏标题
- chat-page-state.ts 加 localStorage helpers 记录当前新建书籍讨论的 sessionId
- ChatPage 初始化时根据 activeBookId 路由决定激活哪条 session

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 02:27:10 -07:00
fanghanjun d49412d6e4 feat(studio): 删掉 LLM 生成 title 逻辑,第一条消息直接写入 title
- 删除 scheduleSessionTitleGeneration / normalizeGeneratedSessionTitle,
  不再异步调 LLM 生成 session 标题
- POST /agent 处理用户消息时,bookSession.title 为 null 就用 instruction
  内容(单行、≤20 字)作为 title,通过 session:title SSE 广播给前端
- POST /sessions 支持接受客户端预生成的 sessionId(格式校验后透传到 core
  的 createAndPersistBookSession),为 draft session 流程做准备
- /services/:id/models 加 10 分钟内存缓存(modelListCache),避免每次
  sidebar 挂载都向真实 LLM 服务发送请求;支持 ?refresh=1 手动刷新
- 测试:修正 createAndPersistBookSession mock 期望参数列表

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 02:26:55 -07:00
fanghanjun b5d14c9b6e refactor(core): session title 简化为"第一条消息即标题" + 老数据 lazy migration
之前的 session title 逻辑用 LLM 生成 + titleSource 三态(draft/ai/manual)过于复杂。
简化为:
- 用户发第一条消息时直接把消息内容(截断≤20 字)写入 title,之后不再覆盖
- 用户手动改名仍然走 renameBookSession
- 删掉 updateSessionTitle/setDraftSessionTitle/titleSource 字段

listBookSessions 加一次性 lazy migration:读到 title=null 但已有用户消息的
老 session,把第一条消息补写为 title 并 persist 回磁盘,下次不再迁移。

createBookSession / createAndPersistBookSession 新增可选 sessionId 参数,
让客户端可以传入预生成的 id,为"新建会话延迟持久化(draft session)"流程
做准备(用户点新建不落盘,发消息时才 POST /sessions 用同一 id 写入)。

测试:
- extractFirstUserMessageTitle 7 个 helper 单测(空数组、缺 user 消息、
  挑首条、空白合并、超长截断、全空白、非数组输入)
- listBookSessions lazy migration 4 个场景(迁移、已有 title 不覆盖、
  无用户消息保持 null、多条同时迁移)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 02:26:45 -07:00
fanghanjun 5ce904708c fix(studio): render ConfirmDialog via portal to escape sidebar containing block
侧边栏的 <aside> 有 backdrop-blur-md,这个 CSS 会创建新的 containing
block,让 position: fixed 的子元素被锁在侧边栏范围内,而不是相对视口定位。
结果是删除会话的确认弹窗显示在侧边栏里而不是全屏遮罩。

改用 React Portal 把弹窗渲染到 document.body,绕开祖先节点的 containing
block。Radix 的 Dialog 组件已经自带 Portal,所以改名弹窗一直是正常的。

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 01:25:13 -07:00
fanghanjun 1516868dbd feat(studio): collapsible book folders in sidebar, default collapsed
- 书默认全部折叠,书名行有 ChevronRight 箭头提示可展开
- 展开后才显示 session 列表和"+ 新建会话"按钮,避免每本书都重复出现这些元素
- 新建会话会自动展开对应的书
- 只有已展开的书会加载 session list,减少启动时的查询压力
- 三点菜单从"0 透明度"改为"opacity-0 → 100"过渡,更自然

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 01:23:51 -07:00
fanghanjun d947356135 perf(core): make listBookSessions read files concurrently and skip Zod parse
listBookSessions 原来串行读所有 session 文件 + 对每个文件做完整的
BookSessionSchema 解析(包括 messages/events/toolExecutions 等大字段),
一本书的会话列表查询耗时 3-5 秒。

改为:
- Promise.all 并发读取所有文件
- 只做 JSON.parse,手动 cherry-pick metadata 字段,不做 Zod 验证
- 返回新的轻量类型 BookSessionSummary(含 messageCount 代替完整 messages)

server.ts 的 GET /sessions 端点顺带简化,不再手工映射字段。

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 01:22:23 -07:00
fanghanjun b7db54e3ea fix(studio): skip session fetch when local messages exist
loadSessionDetail 原本只在 set 合并阶段保护本地消息不被覆盖,
但还是会发起远端请求。改为进入 action 就检查:本地已有消息直接
return,避免在流式中或消息未持久化时把服务器返回的"无用户消息"
版本覆盖过来。

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 01:19:45 -07:00
fanghanjun 6d4f5a5e07 docs: fix 4 review issues in context management plan
- Task 3: Revert fallback object to {provider, modelId} only; adding id/api
  would trick resolveModel into using a fake Model with wrong api field
- Task 1: Import beforeEach/afterEach in test file; consolidate describe block
- Task 5: Add explicit initialState modification with concrete code
- Task 7: Reduce to E2E verification only; remove vague grep instructions

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 01:15:51 -07:00
fanghanjun 1515f69464 style(studio): redesign sidebar book/session list to match Cursor-style layout
- 书名行:去掉 ChevronRight 展开箭头和选中背景,改为 FolderOpen 图标 + 纯文本,默认展开
- Session 行:去掉彩色圆点和 ● 选中符号,改为纯文本 + 右对齐相对时间("3 小时"、"6 天")
- 流式中的 session 用 Loader2 转圈替代时间显示
- 无 session 时显示"暂无聊天"灰色占位
- 选中态靠字色加深和淡背景区分,不用强调色
- 三点菜单默认透明,hover 时才可见
- 整体间距加大,书与书之间用 space-y-4 分隔

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 01:13:40 -07:00
fanghanjun 70f3c4fdbf fix(studio): prevent session list reorder on click
mergeSessionIds 原来把 incoming 放在 existing 前面,导致每次点击
session 触发 loadSessionDetail 时,被点的 session 被移到列表第一个。
改为只在末尾追加不存在的 id,已有的保持原序。

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 01:09:52 -07:00
fanghanjun 81fb2c92e3 fix(studio): move "+ 新建会话" button inside expanded session list
按 spec 要求,"+ 新建会话"按钮从书名行右侧的图标改为
展开后 session 列表内部的最后一项(文字按钮),
和 session 子项视觉层级保持一致。

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 00:59:41 -07:00
fanghanjun c87d55368b fix(studio): remove pendingBookArgs from CreateState, keep only on SessionRuntime
pendingBookArgs 原来同时存在 CreateState(顶层)和 SessionRuntime(per-session)两份,
切换 session 时有数据不一致的风险。现在只保留在 SessionRuntime.pendingBookArgs 上:
- setPendingBookArgs 只写到 sessions[activeSessionId]
- handleCreateBook 只从 session.pendingBookArgs 读取
- activateSession / deleteSession 不再同步顶层字段

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 00:58:17 -07:00
fanghanjun 464a1a43f1 docs: add agent context management implementation plan (7 tasks)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 00:57:10 -07:00
fanghanjun 1e5fefae68 refactor(studio): extract session SSE listeners from App.tsx into use-session-events hook
session:title 和 book:created 的 SSE 事件处理原来写在 App.tsx 里(70 行),
现在抽到 hooks/use-session-events.ts 里,App.tsx 只调 useSessionEvents()。
同时去掉 create/action.ts 里重复定义的 bookKey,统一从 runtime.ts 导入。

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 00:54:45 -07:00
fanghanjun be7830afbe docs: clarify transformContext does not accumulate injected content
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 00:53:03 -07:00
fanghanjun 12ac3a6d44 docs: add agent context management design spec
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 00:48:29 -07:00
Ma 3f74bb7688 chore(release): bump to v1.3.4 and update changelog v1.3.4 2026-04-17 09:59:16 +08:00
Ma 2921456a44 Merge pull request #198 from SttFang/refactor-drop-revise-chapter-tool 2026-04-17 09:43:07 +08:00
Ma 82f120c0ce Merge pull request #197 from SttFang/fix-pin-pi-mono-versions 2026-04-17 09:42:56 +08:00
fanghanjun b358eeb49d fix(studio): stop doctor probe tests from hitting the real /models endpoint
Two doctor tests (reload-fresh-config, auto-fallback to non-stream) let
fetchModelsFromServiceBaseUrl call the real OpenAI /models URL because
no fetch stub was installed. The live endpoint returned 401, which
short-circuited probeServiceCapabilities before createLLMClient or
chatCompletion ran, so the assertions on those mocks failed.

Stubs fetch with a 404 response in both tests so the probe falls
through to the chatCompletion path. Also relaxes the maxTokens
assertion in the reload test, since the production probe uses 2048
(matching the rest of the chatCompletion call sites).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-16 10:52:46 -07:00
fanghanjun a57f9db0d5 refactor(core): drop revise_chapter agent tool, route through sub_agent reviser
The revise_chapter tool and sub_agent(agent="reviser") both wrapped the
same pipeline.reviseDraft() call with an almost-identical parameter set.
Listing both in the system prompt made the model oscillate between them.

Removes createReviseChapterTool, its export, registration, and tests.
Updates the system prompt and edit/write tool descriptions to point at
sub_agent(reviser) for whole-chapter rewrites.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-16 10:52:36 -07:00
fanghanjun bb51987f87 fix(deps): pin @mariozechner/pi-ai and pi-agent-core to 0.67.1
Users on slow npm mirrors (taobao/cnpm/etc.) hit
'No matching version found for @mariozechner/pi-ai@^0.67.4' when
running 'npm install -g @actalk/inkos@latest'. The caret range in
inkos-core ('^0.67.1') resolves to pi-agent-core@0.67.4, whose own
package.json tightens pi-ai to '^0.67.4'. If the mirror has not yet
synced pi-ai@0.67.4, the install breaks.

Add npm 'overrides' to packages/cli/package.json (the published root)
and pnpm.overrides to the workspace root so both end-user installs
and local dev pin to a known-good version. Bumping pi-mono now
becomes a deliberate edit rather than an automatic, mirror-dependent
upgrade.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-16 10:19:13 -07:00
hanjun fang 52250539db Merge pull request #195 from SttFang/feat/sub-agent-params-alignment
feat: sub_agent tool params alignment with pipeline methods
2026-04-17 01:11:38 +08:00
fanghanjun 291f038e59 docs(core): clarify edit/write tool boundaries vs specialised tools
The generic edit and write tools were described abstractly. Spell out
when to reach for specialised tools instead — patch_chapter_text for
chapter text, write_truth_file for canonical truth files,
revise_chapter for chapter revisions — so the agent stops overwriting
chapters or truth files with the generic tools.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-16 10:05:15 -07:00
fanghanjun 31d170a9e0 feat(core): align sub_agent tool params with pipeline runner methods
Extend SubAgentParams schema with explicit architect params (genre,
platform, language, targetChapters) so the architect agent stops
hardcoding initBook config. Auditor now returns issue severity +
description list. Reviser reads mode directly from the schema instead
of regex-matching the instruction. System prompt and tests are updated
in lockstep with the schema.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-16 10:04:09 -07:00
Ma 5619adfa7e feat(core): add generic write tool alongside edit 2026-04-16 23:38:26 +08:00
Ma bca9529c3d feat(core): minimally extend sub_agent control params 2026-04-16 22:16:44 +08:00
Ma 77b8a9a5a8 fix(core): restore generic edit as fallback tool in agent sessions 2026-04-16 22:10:04 +08:00
fanghanjun 964f53a526 fix(studio): remove 50-model cap from test endpoint response
Services like PPIO (71 models) and SiliconFlow (111 models) were
truncated to 50. Return the full list.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 22:07:13 +08:00
Ma 34234df50d fix(studio,core): restore fast model listing and known-model fallbacks 2026-04-16 21:53:07 +08:00
fanghanjun 0bb2138f8d fix(studio): probe returns knownModels when /models is unavailable
When discoveredModels is empty and probe succeeds, return the full
knownModels list (7 for MiniMax) instead of just the probed model.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 21:51:47 +08:00
fanghanjun cabc1e31d3 fix(studio): probe uses service's own knownModels first, not global default
buildModelCandidates prioritized the global configModel (kimi-k2.5)
over service-specific models, causing MiniMax probe to try kimi-k2.5
on MiniMax's endpoint. Now uses knownModels[0] as preferredModel
for services that have them.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 21:51:47 +08:00
fanghanjun 5d6a48d28a perf(studio): revert GET /models from full probe to simple API call
f942c84 changed GET /models to call probeServiceCapabilities which does
chat completion testing — causing zhipu to take 97s and minimax 47s.
GET /models only needs the model list, not a full API health check.
Reverted to simple /models fetch + pi-ai fallback.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 21:51:47 +08:00
fanghanjun 0ec53e00ac perf(studio): skip API probe for services with knownModels
MiniMax GET /models took 47s because it tried the Anthropic endpoint
(no /models) and waited for timeout. Services with knownModels now
return the hardcoded list immediately, skipping the network call.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 21:51:47 +08:00
Ma 26965dd589 fix(core,studio): validate service keys before save and on load 2026-04-16 21:51:47 +08:00
fanghanjun 988a4ce1e4 feat(core): add chapterNumber param to sub_agent tool for auditor/reviser
Previously auditor and reviser always operated on the latest chapter.
Now the agent can pass an explicit chapterNumber so "重写第5章" targets
chapter 5 instead of always hitting the latest.

- Added chapterNumber to SubAgentParams schema
- auditor: pipeline.auditDraft(bookId, chapterNumber)
- reviser: pipeline.reviseDraft(bookId, chapterNumber, mode)
- Updated system prompt to document the parameter

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 21:51:47 +08:00
Ma 0f92a4ef2c docs: refresh WeChat group QR to v7 2026-04-16 20:19:04 +08:00
Ma cfdb200fff chore(release): bump to v1.3.3 and update changelog v1.3.3 2026-04-16 19:36:22 +08:00
Ma 3813cbbf80 fix(core): require explicit title for agent book creation 2026-04-16 17:56:44 +08:00
Ma 974006ebb2 fix(core,cli,studio): unify real epub export implementation 2026-04-16 17:56:44 +08:00
Ma f150609058 test(core): narrow agent prompt deterministic-tool assertion 2026-04-16 15:56:10 +08:00
Ma 542182eae6 feat(cli): route TUI chat through local agent sessions 2026-04-16 15:54:46 +08:00
Ma 7ab07a6fa3 fix(core): force book-mode agent to use deterministic writing tools 2026-04-16 15:54:46 +08:00