@jun133/kitty 0.0.10 → 0.0.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # 小猫智能体 Kitty
2
2
 
3
+ ![demo](site/images/product.png)
4
+
3
5
  官网:https://agentjz.github.io/kitty/
4
6
 
5
7
  <p align="center">
@@ -14,20 +16,23 @@
14
16
  <img alt="agent" src="https://img.shields.io/badge/mode-agent-7c3aed">
15
17
  </p>
16
18
 
17
- 小猫智能体是给本地代码仓库使用的 agent harness。
18
-
19
- 它把模型、工具、上下文、会话、变更记录和验证事实收进一个稳定的本地编程体验里,让长任务可以被推进、保存、恢复和继续。
20
-
21
- 它的主线不是自我改造,而是把本地 coding agent 做成可恢复、可验收、省钱的执行系统:
22
-
23
- - 本地执行内核:聊天只是入口,session、workset、execution、events、memory 和 status 才是任务现场。
24
- - 生产现场:`kitty status` 把当前目标、下一步、阻塞、后台、成本、恢复、skill 和 memory 汇成一眼可读的现场。
25
- - Cost Kernel:即使只用一个模型,也通过稳定前缀、易变尾部、大内容压缩、按需 skill 和 cache usage 审阅来省 token。
26
- - 产品级验收合同:`kitty eval` 验证真实用户路径,而不是只证明模块能 import。
27
-
28
- ## 为什么是小猫智能体
29
-
30
- 小猫智能体的核心体验很明确:
19
+ ![demo](site/images/demo.png)
20
+
21
+
22
+ 小猫智能体是给本地代码仓库使用的 agent harness。
23
+
24
+ 它把模型、工具、上下文、会话、变更记录和验证事实收进一个稳定的本地编程体验里,让长任务可以被推进、保存、恢复和继续。
25
+
26
+ 它的主线不是自我改造,而是把本地 coding agent 做成可恢复、可验收、省钱的执行系统:
27
+
28
+ - 本地执行内核:聊天只是入口,session、workset、execution、events、memory status 才是任务现场。
29
+ - 生产现场:`kitty status` 把当前目标、下一步、阻塞、后台、成本、恢复、skill 和 memory 汇成一眼可读的现场。
30
+ - Cost Kernel:即使只用一个模型,也通过稳定前缀、易变尾部、工具输出治理、按需 skill 和 cache usage 审阅来省 token。
31
+ - 产品级验收合同:`kitty eval` 验证真实用户路径,而不是只证明模块能 import。
32
+
33
+ ## ✨ 为什么是小猫智能体
34
+
35
+ 小猫智能体的核心体验很明确:
31
36
 
32
37
  - 🧭 一个 agent 主循环负责推进任务。
33
38
  - 🛠️ 四个 core 工具完成基础编程闭环。
@@ -42,17 +47,17 @@
42
47
  | 🧭 Agent 循环 | 模型、工具、session、收尾都在同一个主循环里推进 |
43
48
  | 🧠 Context | 近场可见对话、项目上下文、项目地图、运行时上下文、工作记忆、工作集、长上下文压缩和预算报告 |
44
49
  | 💾 Session | 会话记录、checkpoint、todo、工作集、恢复现场、结构化可审阅 memory assets |
45
- | 🗺️ Project Map | 目录、入口、脚本、测试、仓库文档和 git 事实进入短项目地图 |
46
- | 🔌 Provider | OpenAI-compatible provider、请求恢复、连接诊断 |
47
- | ❄️ Cost Kernel | 稳定前缀和易变尾部分离,大输出压缩,skill 默认只给索引,读取 provider usage 里的 cache hit / miss / read / write,status 显示稳定比例和最近请求命中状态 |
48
- | 🛠️ Core tools | `read`、`edit`、`write`、`bash` |
49
- | 🧩 Extensions | `todo`、`worktree`、`network`、`background`、`subagent`、`skills` |
50
- | 🧾 Control plane | SQLite 账本记录 task lifecycle、execution、deadline、输出健康、wait policy、pid、状态和 wake 事实;host 负责等待和恢复 lead |
51
- | 🧯 Production scene | `status` 把 session、background、subagent、memory、skills、cache、wake 和失败边界投影成当前现场、下一步和阻塞原因 |
52
- | 📋 Plan 工作流 | `plan.md` 是当前任务总管,配合 plan skill 管理需求、事实、失败测试、目标、设计、任务、验证和收口 |
50
+ | 🗺️ Project Map | 目录、入口、脚本、测试、仓库文档和 git 事实进入短项目地图 |
51
+ | 🔌 Provider | OpenAI-compatible provider、请求恢复、连接诊断 |
52
+ | ❄️ Cost Kernel | 稳定前缀和易变尾部分离,Tool Output Kernel 保存原始输出并给模型短证据,skill 默认只给索引,读取 provider usage 里的 cache hit / miss / read / write,status 显示稳定比例、工具输出节省和最近请求命中状态 |
53
+ | 🛠️ Core tools | `read`、`edit`、`write`、`bash` |
54
+ | 🧩 Extensions | `todo`、`worktree`、`network`、`background`、`subagent`、`skills` |
55
+ | 🧾 Control plane | SQLite 账本记录 task lifecycle、execution、deadline、输出健康、wait policy、pid、状态和 wake 事实;host 负责等待和恢复 lead |
56
+ | 🧯 Production scene | `status` 把 session、background、subagent、memory、skills、cache、wake 和失败边界投影成当前现场、下一步和阻塞原因 |
57
+ | 📋 Plan 工作流 | `plan.md` 是当前任务总管,配合 plan skill 管理需求、事实、失败测试、目标、设计、任务、验证和收口 |
53
58
  | 💬 产品面 | CLI、交互终端、Telegram 私聊服务 |
54
59
  | 📎 证据记录 | session events、终端日志、崩溃记录、文件变更记录 |
55
- | 🧪 Evaluation | `kitty eval` 暴露产品验收场景,`--run` 会跑本地可验证检查闭环 |
60
+ | 🧪 Evaluation | `kitty eval` 暴露产品验收场景,`--run` 会跑本地可验证检查闭环 |
56
61
 
57
62
  ## ⚡ 快速开始
58
63
 
@@ -69,68 +74,68 @@ npm.cmd run build
69
74
  kitty init
70
75
  ```
71
76
 
72
- 启动交互式 agent:
73
-
74
- ```bash
75
- kitty
76
- ```
77
+ 启动交互式 agent:
77
78
 
78
- 如果已有会话,`kitty` 会先显示最近会话列表:输入 `1` 继续最近会话,输入 `0` 新建会话。没有历史会话时会直接新建。会话标题由模型在第一次真实对话完成后生成,后续保持稳定。
79
-
80
- 启动 Ink TUI:
81
-
82
- ```bash
83
- kitty tui
84
- node dist/cli.js tui
85
- ```
86
-
87
- TUI 是可替换的终端壳层,复用同一套 session、driver、工具和斜杠命令。主区显示用户输入、thinking 和回复;工具、后台任务、subagent 和上下文占用在底部现场区呈现,不灌进主对话区。按键:`Enter` 发送,`Ctrl+J` 换行,`PageUp` / `PageDown` 滚动,`Home` / `End` 跳到顶部 / 底部,鼠标滚轮滚动,`Ctrl+C` 中断当前轮。
88
-
89
- 交互模式支持本地斜杠命令。斜杠命令直接读取本地现场,不发送给模型:
90
-
91
- | 斜杠命令 | 用途 |
92
- | --- | --- |
93
- | `/help` | 查看当前可用斜杠命令 |
94
- | `/status` | 查看当前项目现场 |
95
- | `/background`、`/bg` | 查看后台任务现场 |
96
- | `/memory` | 查看 runtime memory assets |
97
- | `/skills` | 查看 runtime skills 健康状态 |
98
- | `/events` | 查看当前 session 最近事件 |
99
- | `/doctor` | 运行本地配置 preflight |
100
- | `/sessions` | 查看最近会话 |
101
- | `/session` | 查看当前 session id |
102
- | `/copy` | 打印当前 session 对话文本 |
103
- | `/export` | 打印当前 session JSON 快照 |
104
- | `/clear` | 清空 UI shell 的当前输入语义 |
105
- | `/reset` | 清空当前项目运行状态并退出 |
106
- | `/exit`、`quit`、`q` | 退出当前会话 |
107
-
108
- 执行一次明确任务:
79
+ ```bash
80
+ kitty
81
+ ```
82
+
83
+ 如果已有会话,`kitty` 会先显示最近会话列表:输入 `1` 继续最近会话,输入 `0` 新建会话。没有历史会话时会直接新建。会话标题由模型在第一次真实对话完成后生成,后续保持稳定。
84
+
85
+ 启动 Ink TUI:
86
+
87
+ ```bash
88
+ kitty tui
89
+ node dist/cli.js tui
90
+ ```
91
+
92
+ TUI 是可替换的终端壳层,复用同一套 session、driver、工具和斜杠命令。主区显示用户输入、thinking 和回复;工具、后台任务、subagent 和上下文占用在底部现场区呈现,不灌进主对话区。按键:`Enter` 发送,`Ctrl+J` 换行,`PageUp` / `PageDown` 滚动,`Home` / `End` 跳到顶部 / 底部,鼠标滚轮滚动,`Ctrl+C` 中断当前轮。
93
+
94
+ 交互模式支持本地斜杠命令。斜杠命令直接读取本地现场,不发送给模型:
95
+
96
+ | 斜杠命令 | 用途 |
97
+ | --- | --- |
98
+ | `/help` | 查看当前可用斜杠命令 |
99
+ | `/status` | 查看当前项目现场 |
100
+ | `/background`、`/bg` | 查看后台任务现场 |
101
+ | `/memory` | 查看 runtime memory assets |
102
+ | `/skills` | 查看 runtime skills 健康状态 |
103
+ | `/events` | 查看当前 session 最近事件 |
104
+ | `/doctor` | 运行本地配置 preflight |
105
+ | `/sessions` | 查看最近会话 |
106
+ | `/session` | 查看当前 session id |
107
+ | `/copy` | 打印当前 session 对话文本 |
108
+ | `/export` | 打印当前 session JSON 快照 |
109
+ | `/clear` | 清空 UI shell 的当前输入语义 |
110
+ | `/reset` | 清空当前项目运行状态并退出 |
111
+ | `/exit`、`quit`、`q` | 退出当前会话 |
112
+
113
+ 执行一次明确任务:
109
114
 
110
115
  ```bash
111
116
  kitty "检查这个仓库并修复失败测试"
112
117
  ```
113
118
 
114
- ## ⌨️ 常用命令
119
+ ## ⌨️ 常用命令
115
120
 
116
121
  | 命令 | 用途 |
117
122
  | --- | --- |
118
- | `kitty` | 进入默认 agent 交互;有历史会话时先选择继续或新建,也可直接接收一次性 prompt |
119
- | `kitty agent` | 显式进入 agent 模式 |
120
- | `kitty tui` | 进入 Ink 终端工作台,支持主区滚动、底部输入和运行现场 |
121
- | `kitty background` | 查看后台任务;`wait <id>` 等待任务;`stop <id>` 停止任务 |
122
- | `kitty resume [sessionId]` | 恢复最近会话或指定会话 |
123
+ | `kitty` | 进入默认 agent 交互;有历史会话时先选择继续或新建,也可直接接收一次性 prompt |
124
+ | `kitty agent` | 显式进入 agent 模式 |
125
+ | `kitty tui` | 进入 Ink 终端工作台,支持主区滚动、底部输入和运行现场 |
126
+ | `kitty background` | 查看后台任务;`wait <id>` 等待任务;`stop <id>` 停止任务 |
127
+ | `kitty resume [sessionId]` | 恢复最近会话或指定会话 |
123
128
  | `kitty sessions` | 查看最近会话 |
124
129
  | `kitty events [sessionId]` | 查看最近会话或指定会话的机器事件 |
125
130
  | `kitty config show` | 查看从 `.kitty/.env` 解析出的当前运行配置 |
126
131
  | `kitty config path` | 查看当前项目 `.kitty/.env` 路径 |
127
- | `kitty status` | 查看当前项目现场:当前目标、下一步、阻塞、后台、恢复、成本、session、context budget、memory、skills、project map、execution、wake |
128
- | `kitty memory` | 创建、查看、读取、搜索、删除 runtime memory assets,或把 memory 沉淀到 skill references |
132
+ | `kitty status` | 查看当前项目现场:当前目标、下一步、阻塞、后台、恢复、成本、session、context budget、memory、skills、project map、execution、wake |
133
+ | `kitty memory` | 创建、查看、读取、搜索、删除 runtime memory assets,或把 memory 沉淀到 skill references |
129
134
  | `kitty changes` | 查看记录的文件变更 |
130
135
  | `kitty undo [changeId]` | 撤销最近一次或指定变更 |
131
136
  | `kitty diff [path]` | 查看当前 git diff |
132
137
  | `kitty doctor` | 检查 `.kitty` 文件、env contract、provider preset、runtime、provider 连接和下一步 |
133
- | `kitty eval` | 查看产品验收场景;`kitty eval --run` 运行本地机器验收 |
138
+ | `kitty eval` | 查看产品验收场景;`kitty eval --run` 运行本地机器验收 |
134
139
  | `kitty telegram serve` | 启动 Telegram 私聊服务 |
135
140
 
136
141
  ## 🛠️ 工具体系
@@ -151,27 +156,27 @@ Extension 是可启用、可禁用、独立存在的工具集合:
151
156
  | `todo` | 会话级 todo 写入和可见 checklist |
152
157
  | `worktree` | Git worktree 事实、创建、保留和删除 |
153
158
  | `network` | HTTP session、请求、探测、下载、trace、OpenAPI 检查 |
154
- | `background` | 后台命令执行、运行输出摘要、deadline、last output、检查、等待、停止和生命周期记录 |
155
- | `subagent` | 聚焦子执行启动、派工边界、deadline、状态检查、worker 结论回传、wait policy、lead 挂起与 wake 恢复 |
156
- | `skills` | 项目运行时 skill 包索引、正文加载、资源读取和脚本运行;使用事实进入 observability 和 task lifecycle |
157
-
158
- 默认 agent 会启用 `todo`、`worktree`、`network`、`background`、`subagent`、`skills`。
159
+ | `background` | 后台命令执行、运行输出摘要、deadline、last output、检查、等待、停止和生命周期记录 |
160
+ | `subagent` | 聚焦子执行启动、派工边界、deadline、状态检查、worker 结论回传、wait policy、lead 挂起与 wake 恢复 |
161
+ | `skills` | 项目运行时 skill 包索引、正文加载、资源读取和脚本运行;使用事实进入 observability 和 task lifecycle |
162
+
163
+ 默认 agent 会启用 `todo`、`worktree`、`network`、`background`、`subagent`、`skills`。
159
164
 
160
165
  Runtime skills 放在项目 `SKILL.md`、`.skills/**/SKILL.md` 或 `skills/**/SKILL.md`。默认上下文只显示 skill 名称、说明、路径、健康状态和资源索引;完整正文必须由模型明确调用 `skill_load` 后进入当前轮。Skill 包内的 `references/`、`scripts/`、`examples/` 和 `assets/` 会作为资源分组出现,需要时用 `skill_read_resource` 读取资源,或用 `skill_run_script` 运行已声明的 `scripts/` 资源。Skill frontmatter 可用 `requires` 声明命令依赖,运行时用 `skill_check` 检查包健康和依赖可用性。`.codex/skills/**` 是 Codex 维护本仓库用的开发规范,不属于小猫运行时 skill。
161
166
 
162
- 当前仓库内置四个开发阶段 runtime skill:`research`、`plan`、`do`、`verification`。其中 `plan` 强制把 `plan.md` 写成单文件规格驱动执行合同:需求文档、当前事实、失败测试、目标、不做范围、设计、实施任务、验证计划和收口记录都在一个文件里闭环。
167
+ 当前仓库内置四个开发阶段 runtime skill:`research`、`plan`、`do`、`verification`。其中 `plan` 强制把 `plan.md` 写成单文件规格驱动执行合同:需求文档、当前事实、失败测试、目标、不做范围、设计、实施任务、验证计划和收口记录都在一个文件里闭环。
163
168
 
164
- Provider 请求优先携带同 session 的近场可见对话。短会话不靠账本拼上下文;长会话超预算时摘要旧对话,保留最近对话 tail。Session memory 由模型在 turn 收口时按固定 Markdown 区块写出:`Current Focus`、`User Constraints`、`Decisions`、`Open Threads`、`Verification Facts`、`Reusable Lessons`。机器只维护格式和保存边界,不替模型判断事实重要性。
165
-
166
- Cost Kernel 的边界很硬:省钱不靠模型路由,不靠把能力关掉,而靠上下文结构。稳定内容放在前缀,易变事实放在尾部;大段工具输出和旧历史进入压缩摘要或证据资产;skill 正文、resources、examples 不默认注入,模型需要时再加载。
167
-
168
- Provider usage 会归一化缓存事实:DeepSeek 的 `prompt_cache_hit_tokens` / `prompt_cache_miss_tokens`,OpenAI 的 `prompt_tokens_details.cached_tokens`,Anthropic 的 `cache_read_input_tokens` / `cache_creation_input_tokens`,以及 Gemini cached content tokens。`model.request` observability 事件会记录这些字段,`kitty status` 会显示最近模型请求的缓存命中和 context cache layout。OpenAI 请求会使用同 session 稳定 `prompt_cache_key`;DeepSeek 不写无效 `cache_control`,优先保持稳定前缀和命中观测。
169
-
170
- `kitty eval` 是产品验收合同:每个场景都说明用户路径和机器证据。`kitty eval --run` 包含生产现场、恢复演练、远程入口、cache economy、skill/memory readiness 和失败边界检查。usage 字段解析、provider cache policy、stable prefix fingerprint、volatile tail、skill index boundary 和大输出压缩都必须能机器验证。真实省钱仍取决于 provider 是否返回 usage,以及同一 session 的前缀是否真的被上游缓存命中。
169
+ Provider 请求优先携带同 session 的近场可见对话。短会话不靠账本拼上下文;长会话超预算时摘要旧对话,保留最近对话 tail。Session memory 由模型在 turn 收口时按固定 Markdown 区块写出:`Current Focus`、`User Constraints`、`Decisions`、`Open Threads`、`Verification Facts`、`Reusable Lessons`。机器只维护格式和保存边界,不替模型判断事实重要性。
170
+
171
+ Cost Kernel 的边界很硬:省钱不靠模型路由,不靠把能力关掉,而靠上下文结构。稳定内容放在前缀,易变事实放在尾部;Tool Output Kernel 把原始工具输出保存成可恢复证据,把测试、构建、typecheck、搜索和 git diff 输出投影成短证据,再记录估算节省;skill 正文、resources、examples 不默认注入,模型需要时再加载。
171
172
 
172
- Session workset 记录当前会话实际读过和改过的文件。`read` 成功后记录读取事实,`edit` / `write` 成功后记录变更事实和 change id。工作集会进入 session、working memory `kitty status`,让用户看到当前任务真正碰过哪些文件。
173
+ Provider usage 会归一化缓存事实:DeepSeek 的 `prompt_cache_hit_tokens` / `prompt_cache_miss_tokens`,OpenAI 的 `prompt_tokens_details.cached_tokens`,Anthropic 的 `cache_read_input_tokens` / `cache_creation_input_tokens`,以及 Gemini cached content tokens。`model.request` observability 事件会记录这些字段,`tool.output` observability 事件会记录工具输出原始 token 估算、投影 token 估算、节省比例、是否截断和完整输出路径。`kitty status` 会显示最近模型请求的缓存命中、context cache layout 和工具输出治理现场。OpenAI 请求会使用同 session 稳定 `prompt_cache_key`;DeepSeek 不写无效 `cache_control`,优先保持稳定前缀和命中观测。
174
+
175
+ `kitty eval` 是产品验收合同:每个场景都说明用户路径和机器证据。`kitty eval --run` 包含生产现场、恢复演练、远程入口、cache economy、skill/memory readiness 和失败边界检查。usage 字段解析、provider cache policy、stable prefix fingerprint、volatile tail、skill index boundary 和大输出压缩都必须能机器验证。真实省钱仍取决于 provider 是否返回 usage,以及同一 session 的前缀是否真的被上游缓存命中。
176
+
177
+ Session workset 记录当前会话实际读过和改过的文件。`read` 成功后记录读取事实,`edit` / `write` 成功后记录变更事实和 change id。工作集会进入 session、working memory 和 `kitty status`,让用户看到当前任务真正碰过哪些文件。
173
178
 
174
- Memory assets 分为 `session`、`project`、`user` 和 `evidence`。每条 asset 暴露 kind、id、title、scope、tags、路径和 evidence references。Session memory 由模型写,project/user/evidence assets 通过 `kitty memory --create <kind> --title <title> --content <content>` 创建成可审阅 Markdown 资产。`kitty memory -q <query>` 做多词候选召回,只返回命中的资产和证据行,不替模型判断语义重要性。`kitty memory <memoryId> --append-to-skill <skillName>` 可以写入该 skill 的 `references/`。这条路径只沉淀已保存事实,不替模型判断哪些经验值得复用。
179
+ Memory assets 分为 `session`、`project`、`user` 和 `evidence`。每条 asset 暴露 kind、id、title、scope、tags、路径和 evidence references。Session memory 由模型写,project/user/evidence assets 通过 `kitty memory --create <kind> --title <title> --content <content>` 创建成可审阅 Markdown 资产。`kitty memory -q <query>` 做多词候选召回,只返回命中的资产和证据行,不替模型判断语义重要性。`kitty memory <memoryId> --append-to-skill <skillName>` 可以写入该 skill 的 `references/`。这条路径只沉淀已保存事实,不替模型判断哪些经验值得复用。
175
180
 
176
181
  查看配置:
177
182
 
@@ -213,11 +218,11 @@ kitty config show
213
218
  | Provider / Config | `src/provider/`, `src/config/` |
214
219
  | Core tools | `src/tools/` |
215
220
  | Extensions | `src/extensions/` |
216
- | Runtime skills | `src/skills/`, `skills/` |
217
- | Project map | `src/project/map.ts` |
218
- | Memory assets | `.kitty/memory/sessions/`, `.kitty/memory/project/`, `.kitty/memory/user/`, `.kitty/memory/evidence/` |
219
- | Control plane | `src/control/`, `src/execution/` |
220
- | Host 边界 | `src/host/` |
221
+ | Runtime skills | `src/skills/`, `skills/` |
222
+ | Project map | `src/project/map.ts` |
223
+ | Memory assets | `.kitty/memory/sessions/`, `.kitty/memory/project/`, `.kitty/memory/user/`, `.kitty/memory/evidence/` |
224
+ | Control plane | `src/control/`, `src/execution/` |
225
+ | Host 边界 | `src/host/` |
221
226
  | Session events | `.kitty/events/`, `src/session/events.ts` |
222
227
  | CLI / Shell / Telegram | `src/cli/`, `src/shell/`, `src/telegram/` |
223
228
  | Runtime UI | `src/runtime-ui/` |
@@ -225,7 +230,7 @@ kitty config show
225
230
  | Evaluation | `src/evaluation/`, `tests/evaluation/` |
226
231
  | 产品宣传页 | `site/` |
227
232
  | GitHub Pages 发布 | `.github/workflows/pages.yml` |
228
- | 项目文档 | `spec/` |
233
+ | 项目文档 | `spec/` |
229
234
  | Tests | `tests/` |
230
235
 
231
236
  ## 🧪 开发
@@ -255,5 +260,5 @@ npm.cmd run verify
255
260
  - `spec/用户审阅/宪法原则/`
256
261
  - `spec/技术实现/`
257
262
 
258
- 项目文档、代码和测试共同描述同一个当前现实。项目入口、配置、文档和测试都跟随当前实现同步维护。
263
+ 项目文档、代码和测试共同描述同一个当前现实。项目入口、配置、文档和测试都跟随当前实现同步维护。
259
264
 
@@ -2,7 +2,7 @@ import {
2
2
  TRANSCRIPT_OUTER_PADDING_X,
3
3
  TUI_COLORS,
4
4
  renderTranscriptLineViews
5
- } from "./chunk-DFDOKON5.mjs";
5
+ } from "./chunk-4HIVDFN5.mjs";
6
6
 
7
7
  // src/shell/tui/layout.ts
8
8
  var TUI_MIN_WIDTH = 48;
@@ -385,7 +385,7 @@ function readFactColor(value) {
385
385
  function createTranscriptComponent(kit) {
386
386
  const { React, Box, Text } = kit;
387
387
  return function Transcript(props) {
388
- const rows = renderTranscriptLineViews(props.state.transcript, props.viewport.width).slice(props.state.scroll.offset, props.state.scroll.offset + props.viewport.height);
388
+ const rows = props.controller ? props.controller.getVisibleTranscriptLineViews(props.viewport) : renderTranscriptLineViews(props.state.transcript, props.viewport.width).slice(props.state.scroll.offset, props.state.scroll.offset + props.viewport.height);
389
389
  return React.createElement(
390
390
  Box,
391
391
  {
@@ -425,16 +425,50 @@ function renderTranscriptLine(React, Box, Text, row) {
425
425
  Text,
426
426
  { color: row.style.text, wrap: "truncate-end" },
427
427
  React.createElement(Text, { color: TUI_COLORS.thought, italic: row.style.italicPrefix, wrap: "truncate-end" }, row.prefix),
428
- row.text
428
+ ...renderTranscriptSpans(React, Text, row)
429
429
  ) : React.createElement(Text, {
430
430
  color: row.style.text,
431
431
  bold: row.style.bold,
432
432
  dimColor: row.style.dim,
433
433
  wrap: "truncate-end"
434
- }, row.text)
434
+ }, ...renderTranscriptSpans(React, Text, row))
435
435
  )
436
436
  );
437
437
  }
438
+ function renderTranscriptSpans(React, Text, row) {
439
+ const spans = row.spans.length > 0 ? row.spans : [createPlainSpan(row.text)];
440
+ return spans.map((span, index) => React.createElement(Text, {
441
+ key: `${row.id}-span-${index}`,
442
+ color: readSpanColor(row, span),
443
+ backgroundColor: span.code ? TUI_COLORS.panelStrong : void 0,
444
+ bold: row.style.bold || span.bold,
445
+ dimColor: row.style.dim || span.dim,
446
+ italic: span.italic,
447
+ strikethrough: span.strike,
448
+ underline: Boolean(span.href),
449
+ wrap: "truncate-end"
450
+ }, span.text));
451
+ }
452
+ function readSpanColor(row, span) {
453
+ if (span.href) {
454
+ return TUI_COLORS.warning;
455
+ }
456
+ if (span.code) {
457
+ return TUI_COLORS.system;
458
+ }
459
+ return row.style.text;
460
+ }
461
+ function createPlainSpan(text) {
462
+ return {
463
+ text,
464
+ bold: false,
465
+ italic: false,
466
+ code: false,
467
+ dim: false,
468
+ strike: false,
469
+ href: void 0
470
+ };
471
+ }
438
472
 
439
473
  // src/shell/tui/components/App.ts
440
474
  function createTuiAppComponent(kit) {
@@ -483,7 +517,7 @@ function createTuiAppComponent(kit) {
483
517
  return React.createElement(
484
518
  Box,
485
519
  { flexDirection: "column", width, height },
486
- React.createElement(Transcript, { state, viewport: transcriptViewport }),
520
+ React.createElement(Transcript, { controller: props.controller, state, viewport: transcriptViewport }),
487
521
  React.createElement(
488
522
  Box,
489
523
  {