@epoch-agent/view 4.1.1 → 5.0.0
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 +25 -7
- package/dist/index.d.ts +85 -2
- package/dist/index.js +38 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -13,13 +13,31 @@
|
|
|
13
13
|
|
|
14
14
|
## 导出
|
|
15
15
|
|
|
16
|
-
| 导出
|
|
17
|
-
|
|
|
18
|
-
| `applyStreamEvent(state, event)`
|
|
19
|
-
| `
|
|
20
|
-
| `
|
|
21
|
-
| `
|
|
22
|
-
| `
|
|
16
|
+
| 导出 | 说明 |
|
|
17
|
+
| --------------------------------------------------------- | -------------------------------------------------------------------------- |
|
|
18
|
+
| `applyStreamEvent(state, event)` | 折叠一个 `AgentEvent`,返回新的 item 列表 + 变更信息 |
|
|
19
|
+
| `activityOf(toolName)` | 工具名 → 人类动作类(`read` / `command` / `web` …)**(转发 protocol)** |
|
|
20
|
+
| `liveDetail(request)` | 从入参里挑一句人话(`description > command > path`…)**(转发 protocol)** |
|
|
21
|
+
| `runningToolActivity(tools)` / `currentActivity(pending)` | 此刻在跑的那条 → 类别 + 细节 |
|
|
22
|
+
| `ACTIVITY_KEYS` | 类别 → catalog key(epoch 两个宿主共用的唯一真源) |
|
|
23
|
+
| `diffLines(before, after)` | 行级 diff |
|
|
24
|
+
| `collapseContext(lines)` | 把连续的未改动行折叠成 `@@` 摘要 |
|
|
25
|
+
| `DIFF_CONTEXT_LINES` / `DIFF_MAX_LINES` | 折叠与截断的阈值(后者的口径见下) |
|
|
26
|
+
| `HistoryItem*` / `ToolDisplayItem` 等类型 | item 的判别联合 |
|
|
27
|
+
|
|
28
|
+
## 「现在在干什么」(2026-11-09,2026-11-10 判据下移)
|
|
29
|
+
|
|
30
|
+
**类别与细节的判据住在 [protocol 的 activity.ts](../protocol/src/activity.ts)**:
|
|
31
|
+
答这个问题的除 `tui` / `web`,还有网线另一头的第三方宿主(宿主桌宠、托盘、状态栏),
|
|
32
|
+
而它们拿不到 `view`。`core` 发 `tool-call` 帧时用同一张表算好塞进 `activity` 字段,
|
|
33
|
+
所以「`terminal` 算哪一类」全仓只有一个答案。这一层再导出一次,`tui` / `web` 的名字没变。
|
|
34
|
+
|
|
35
|
+
留在这里的是**需要展示项形状**的那一半:`currentActivity()` 只认末尾那个工具组里
|
|
36
|
+
还在跑的那条(并行时先回来的那个会留在原组里,照它画等于说到十分钟前的状态),
|
|
37
|
+
以及 `ACTIVITY_KEYS`(类别 → epoch 自己那份 catalog 的 key)。
|
|
38
|
+
|
|
39
|
+
⚠️ 它和 `web` 那边的 `live-verb.ts` 是两件事:那一族是「说法的轮换」(同一档状态
|
|
40
|
+
换着词说,信息量为零),这里是「事实的陈述」。**事实不能轮换。**
|
|
23
41
|
|
|
24
42
|
## 展示项的 `at`:那句话是什么时候说的(2026-10-27)
|
|
25
43
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { WireSessionReference, WireFileReference, WireImageReference, EpochUserCommand, ToolArtifact, AgentEvent } from '@epoch-agent/protocol';
|
|
1
|
+
import { WireSessionReference, WireFileReference, WireImageReference, EpochUserCommand, ToolArtifact, AgentEvent, ActivityKind, ToolActivity } from '@epoch-agent/protocol';
|
|
2
|
+
export { ActivityKind, ToolActivity, activityOf, liveDetail } from '@epoch-agent/protocol';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* 展示项类型 — 基于 Google Gemini CLI types.ts
|
|
@@ -115,6 +116,21 @@ type HistoryItemUser = HistoryItemBase & {
|
|
|
115
116
|
* 缺席 = 普通消息,或者这条是 v15 之前的库写的(推不出来就不编)。
|
|
116
117
|
*/
|
|
117
118
|
command?: EpochUserCommand;
|
|
119
|
+
/**
|
|
120
|
+
* 这一条**点名了哪条技能**(2026-11-09,会话库 v18)。
|
|
121
|
+
*
|
|
122
|
+
* 和 `command` 同一个位置、同一条判据:`text` 是用户打的那段字,而「他说要用
|
|
123
|
+
* 哪条技能」不在里面(技能正文走的是 system prompt 里的 `<pinned_skill>`)。
|
|
124
|
+
* 渲染层据这一格在气泡旁边画一枚「技能 · X」的小标记 —— 用户点了技能之后
|
|
125
|
+
* 得在对话里看得见自己点过,否则和「按了没反应」分不出来。
|
|
126
|
+
*
|
|
127
|
+
* 三个来源各有一条填它的路,逐字同 `command`:回放取 `messages.skill` 那一列 ·
|
|
128
|
+
* 刚发出去那一条取 `submit` action · 别的窗口发的那一条取 `user-message` 帧上的
|
|
129
|
+
* `skill`。
|
|
130
|
+
*
|
|
131
|
+
* 缺席 = 没点名技能,或者这条是 v18 之前的库写的。
|
|
132
|
+
*/
|
|
133
|
+
skill?: string;
|
|
118
134
|
};
|
|
119
135
|
/**
|
|
120
136
|
* 模型的回复正文。
|
|
@@ -296,6 +312,73 @@ interface StreamEventMeta {
|
|
|
296
312
|
*/
|
|
297
313
|
declare function applyStreamEvent(state: StreamReduceResult, event: AgentEvent, meta?: StreamEventMeta): StreamReduceResult;
|
|
298
314
|
|
|
315
|
+
/**
|
|
316
|
+
* 「这一轮现在在干什么」—— 折叠**这一侧**该管的那一半。
|
|
317
|
+
*
|
|
318
|
+
* ## 分家(2026-11-10)
|
|
319
|
+
*
|
|
320
|
+
* 判据(工具名 → 类别、入参 → 一句细节)**搬到 `@epoch-agent/protocol` 了**:
|
|
321
|
+
* 答这个问题的除了 `tui` 和 `web`,还有网线另一头的第三方宿主(桌宠、托栏、状态栏),
|
|
322
|
+
* 而它们拿不到 `view`。表只留一份,`core` 发 `tool-call` 帧时顺手算好塞进
|
|
323
|
+
* `activity` 字段,谁都能读。
|
|
324
|
+
*
|
|
325
|
+
* 留在这儿的只有**需要展示项**的那一半:
|
|
326
|
+
*
|
|
327
|
+
* - {@link runningToolActivity} / {@link currentActivity}:从 `tool_group` 里挑
|
|
328
|
+
* 「此刻还在跑的那条」。判据是「只认末尾那个工具组」—— 那需要 `ToolDisplayItem`
|
|
329
|
+
* 的形状,是展示层的事;
|
|
330
|
+
* - {@link ACTIVITY_KEYS}:类别 → epoch 自己的 catalog key(第三方宿主用自己那份)。
|
|
331
|
+
*
|
|
332
|
+
* ⚠️ **和 `web/src/thread/live-verb.ts` 那两族文案是两件事**:那两族是「说法的
|
|
333
|
+
* 轮换」(同一档状态换着词说,信息量为零),这里是「事实的陈述」(它真的在读文件)。
|
|
334
|
+
* 所以这里必须**给定**,不能轮换 —— 轮换事实就成了撒谎。
|
|
335
|
+
*/
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* 一组调用里**此刻还在跑**的那条 → 活动。
|
|
339
|
+
*
|
|
340
|
+
* 取最后一条而不是第一条:并行三条时用户要看的是最近发起的那条(前两条早就
|
|
341
|
+
* 在跑、屏幕上那一行已经说过它了)。
|
|
342
|
+
*/
|
|
343
|
+
declare function runningToolActivity(tools: readonly ToolDisplayItem[]): ToolActivity | undefined;
|
|
344
|
+
/**
|
|
345
|
+
* `currentActivity` 读得动的最小形状。
|
|
346
|
+
*
|
|
347
|
+
* **故意比 `HistoryItemWithoutId` 宽**:TUI 的展示项是它的超集(多一个 `plan`
|
|
348
|
+
* 那种只在终端存在的项),而这里根本不关心别的类型 —— 写窄了等于逼那个包
|
|
349
|
+
* 把一整棵联合类型递进来,再在这里写一遍它不认识的分支。
|
|
350
|
+
*/
|
|
351
|
+
interface ActivitySource {
|
|
352
|
+
readonly type: string;
|
|
353
|
+
readonly tools?: readonly ToolDisplayItem[];
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* 流式列表里「此刻在干什么」。
|
|
357
|
+
*
|
|
358
|
+
* ⚠️ **只认末尾那个工具组**:更早的组里若还有 `pending`,那是结果没回来的残留
|
|
359
|
+
* (并行时先回来的那个会留在原组里),照着它画等于把一句话说成十分钟前的状态。
|
|
360
|
+
* 末尾那组都跑完了就**什么都不说** —— 那时模型在想,宿主该回到「在想」那一档。
|
|
361
|
+
*/
|
|
362
|
+
declare function currentActivity(pending: readonly ActivitySource[]): ToolActivity | undefined;
|
|
363
|
+
/**
|
|
364
|
+
* 类别 → catalog key。
|
|
365
|
+
*
|
|
366
|
+
* ⚠️ **这张表是 epoch 两个宿主的唯一真源**:终端那一行和浏览器那一行说的是同一件事,
|
|
367
|
+
* 各抄一份等于同一句话有两个家。key 以**字面量**躺在源码里,i18n 门禁
|
|
368
|
+
* (`__tests__/i18n-catalog.test.ts`)就是靠这一点确认它们「被用过」的 —— 别改成
|
|
369
|
+
* 拼出来的 key。
|
|
370
|
+
*
|
|
371
|
+
* ⚠️ 第三方宿主**不要**用它:这是 epoch 自己那份 catalog 的 key,别的宿主要说的话,
|
|
372
|
+
* 按 `ActivityKind` 翻自己那份(`nebular-agent` 的 `pet.activity.<kind>` 就是这么做的)。
|
|
373
|
+
*
|
|
374
|
+
* ⚠️ `other` 那一档宿主**不要用它**:认不出来的工具(MCP 等)显示生名比显示
|
|
375
|
+
* 「正在调用工具」有用得多。它在这儿只是为了表是满的。
|
|
376
|
+
*/
|
|
377
|
+
declare const ACTIVITY_KEYS: Readonly<Record<ActivityKind, {
|
|
378
|
+
readonly live: string;
|
|
379
|
+
readonly done: string;
|
|
380
|
+
}>>;
|
|
381
|
+
|
|
299
382
|
/**
|
|
300
383
|
* 行级 diff —— 纯函数,可单测。
|
|
301
384
|
*
|
|
@@ -382,4 +465,4 @@ declare const DIFF_CONTEXT_LINES = 1;
|
|
|
382
465
|
*/
|
|
383
466
|
declare function collapseContext(lines: readonly DiffLine[], context?: number): Array<DiffLine | null>;
|
|
384
467
|
|
|
385
|
-
export { DIFF_CONTEXT_LINES, DIFF_MAX_LINES, type DiffLine, type DiffLineKind, type DiffResult, type HistoryItem, type HistoryItemAssistant, type HistoryItemBase, type HistoryItemCompression, type HistoryItemDiff, type HistoryItemError, type HistoryItemHint, type HistoryItemInfo, type HistoryItemModel, type HistoryItemSessionSummary, type HistoryItemThinking, type HistoryItemToolDisplayGroup, type HistoryItemToolGroup, type HistoryItemUser, type HistoryItemWarning, type HistoryItemWithoutId, type StreamEventMeta, type StreamReduceResult, type ToolDisplayItem, applyStreamEvent, collapseContext, diffLines };
|
|
468
|
+
export { ACTIVITY_KEYS, DIFF_CONTEXT_LINES, DIFF_MAX_LINES, type DiffLine, type DiffLineKind, type DiffResult, type HistoryItem, type HistoryItemAssistant, type HistoryItemBase, type HistoryItemCompression, type HistoryItemDiff, type HistoryItemError, type HistoryItemHint, type HistoryItemInfo, type HistoryItemModel, type HistoryItemSessionSummary, type HistoryItemThinking, type HistoryItemToolDisplayGroup, type HistoryItemToolGroup, type HistoryItemUser, type HistoryItemWarning, type HistoryItemWithoutId, type StreamEventMeta, type StreamReduceResult, type ToolDisplayItem, applyStreamEvent, collapseContext, currentActivity, diffLines, runningToolActivity };
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { liveDetail, activityOf } from '@epoch-agent/protocol';
|
|
2
|
+
export { activityOf, liveDetail } from '@epoch-agent/protocol';
|
|
3
|
+
|
|
1
4
|
// src/reducer.ts
|
|
2
5
|
function anchor(meta) {
|
|
3
6
|
return {
|
|
@@ -127,6 +130,40 @@ function applyStreamEvent(state, event, meta) {
|
|
|
127
130
|
}
|
|
128
131
|
}
|
|
129
132
|
}
|
|
133
|
+
function runningToolActivity(tools) {
|
|
134
|
+
const running = tools.filter((tool) => tool.status === "pending");
|
|
135
|
+
const last = running[running.length - 1];
|
|
136
|
+
if (last === void 0) return void 0;
|
|
137
|
+
const detail = liveDetail(last.request);
|
|
138
|
+
return {
|
|
139
|
+
kind: activityOf(last.toolName),
|
|
140
|
+
...detail !== void 0 ? { detail } : {}
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
function currentActivity(pending) {
|
|
144
|
+
for (let i = pending.length - 1; i >= 0; i--) {
|
|
145
|
+
const item = pending[i];
|
|
146
|
+
if (item?.type !== "tool_group" && item?.type !== "tool_display_group") continue;
|
|
147
|
+
return runningToolActivity(item.tools ?? []);
|
|
148
|
+
}
|
|
149
|
+
return void 0;
|
|
150
|
+
}
|
|
151
|
+
var ACTIVITY_KEYS = {
|
|
152
|
+
read: { live: "activity.read", done: "activity.read_done" },
|
|
153
|
+
read_image: { live: "activity.read_image", done: "activity.read_image_done" },
|
|
154
|
+
write: { live: "activity.write", done: "activity.write_done" },
|
|
155
|
+
edit: { live: "activity.edit", done: "activity.edit_done" },
|
|
156
|
+
search: { live: "activity.search", done: "activity.search_done" },
|
|
157
|
+
command: { live: "activity.command", done: "activity.command_done" },
|
|
158
|
+
shell: { live: "activity.shell", done: "activity.shell_done" },
|
|
159
|
+
code: { live: "activity.code", done: "activity.code_done" },
|
|
160
|
+
web: { live: "activity.web", done: "activity.web_done" },
|
|
161
|
+
team: { live: "activity.team", done: "activity.team_done" },
|
|
162
|
+
schedule: { live: "activity.schedule", done: "activity.schedule_done" },
|
|
163
|
+
plan: { live: "activity.plan", done: "activity.plan_done" },
|
|
164
|
+
ask: { live: "activity.ask", done: "activity.ask_done" },
|
|
165
|
+
other: { live: "activity.other", done: "activity.other_done" }
|
|
166
|
+
};
|
|
130
167
|
// src/diff-lines.ts
|
|
131
168
|
var DIFF_MAX_LINES = 2e3;
|
|
132
169
|
function splitLines(text) {
|
|
@@ -219,4 +256,4 @@ function collapseContext(lines, context = DIFF_CONTEXT_LINES) {
|
|
|
219
256
|
return out;
|
|
220
257
|
}
|
|
221
258
|
|
|
222
|
-
export { DIFF_CONTEXT_LINES, DIFF_MAX_LINES, applyStreamEvent, collapseContext, diffLines };
|
|
259
|
+
export { ACTIVITY_KEYS, DIFF_CONTEXT_LINES, DIFF_MAX_LINES, applyStreamEvent, collapseContext, currentActivity, diffLines, runningToolActivity };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@epoch-agent/view",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.0.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "epoch-agent 展示层纯逻辑:AgentEvent → 可渲染 item,tui 与 web 共用",
|
|
6
6
|
"repository": {
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"dist"
|
|
23
23
|
],
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@epoch-agent/protocol": "
|
|
25
|
+
"@epoch-agent/protocol": "5.0.0"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"tsup": "^8.3.6",
|