@epoch-agent/view 5.0.0 → 6.1.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 +9 -5
- package/dist/index.d.ts +34 -0
- package/dist/index.js +15 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -78,10 +78,14 @@
|
|
|
78
78
|
|
|
79
79
|
```bash
|
|
80
80
|
pnpm --filter @epoch-agent/view test
|
|
81
|
-
pnpm --filter @epoch-agent/view build #
|
|
81
|
+
pnpm --filter @epoch-agent/view build # 应用侧(vite build / tsx)从 dist 解析它
|
|
82
82
|
```
|
|
83
83
|
|
|
84
|
-
> ⚠️ [web](../web) 和 [tui](../tui)
|
|
85
|
-
>
|
|
86
|
-
> `vite build` 报 `MISSING_EXPORT
|
|
87
|
-
>
|
|
84
|
+
> ⚠️ [web](../web) 和 [tui](../tui) 的**应用侧**(`vite build`、`tsx`)是按
|
|
85
|
+
> package.json 的 `exports` 从**本包的 `dist/`** 解析的。只改源码不 build,那边看到的
|
|
86
|
+
> 还是上一次的产物——症状是 `vite build` 报 `MISSING_EXPORT`,或者新加的字段在界面上
|
|
87
|
+
> 永远不出现。根目录的 `pnpm build` 会自己排好顺序。
|
|
88
|
+
>
|
|
89
|
+
> ⚠️ **用例不是这一档**:web / tui 的 `vitest.config.ts` 把 `@epoch-agent/view`
|
|
90
|
+
> alias 到 `src/index.ts`,所以 `pnpm test` 不需要先 build。判据和实测在那两份配置的
|
|
91
|
+
> 注释里——那之前 `pnpm test` 读的是 `dist`,改了源码没重建时全绿但测的是旧代码。
|
package/dist/index.d.ts
CHANGED
|
@@ -47,6 +47,24 @@ interface HistoryItemBase {
|
|
|
47
47
|
* 第一次调用)—— 用最后一帧会让回退目标往后漂。
|
|
48
48
|
*/
|
|
49
49
|
seq?: number;
|
|
50
|
+
/**
|
|
51
|
+
* 这一项对应那条**落盘消息**的主键(`messages.id`)。
|
|
52
|
+
*
|
|
53
|
+
* ## 它和 `seq` / `HistoryItem.id` 都不是一回事
|
|
54
|
+
*
|
|
55
|
+
* | 钥匙 | 谁发的 | 什么时候有 | 活多久 |
|
|
56
|
+
* | --- | --- | --- | --- |
|
|
57
|
+
* | `seq` | 服务进程的全局信封计数器 | 直播 | 进程重启就从 1 重新数 |
|
|
58
|
+
* | `id`(`HistoryItem.id`) | 浏览器自己的目录计数器 | 直播和回放都有 | 一次补拉就可能换 |
|
|
59
|
+
* | `msgId` | 会话库的主键 | **只有回放** | 永远不变 |
|
|
60
|
+
*
|
|
61
|
+
* 靠它做成的是「跨页稳定锚点」:历史分页之后,同一句提问在服务端那侧永远是
|
|
62
|
+
* 同一个 `msgId`,而本地 `id` 会随前置页平移。右侧刻度条落在
|
|
63
|
+
* `data-rail="m<msgId>"` 上就是这一格(判据在 web 的 `thread/rail.ts`)。
|
|
64
|
+
*
|
|
65
|
+
* 缺席是常态:直播那条路上没有它(帧里不带落盘主键),TUI 也从不填。
|
|
66
|
+
*/
|
|
67
|
+
msgId?: number;
|
|
50
68
|
}
|
|
51
69
|
type HistoryItemUser = HistoryItemBase & {
|
|
52
70
|
type: 'user';
|
|
@@ -178,6 +196,22 @@ type HistoryItemThinking = HistoryItemBase & {
|
|
|
178
196
|
type HistoryItemDiff = HistoryItemBase & {
|
|
179
197
|
type: 'diff';
|
|
180
198
|
filePath: string;
|
|
199
|
+
/**
|
|
200
|
+
* 产出这张补丁的那次调用(`tool-call` 的 `callId`)。
|
|
201
|
+
*
|
|
202
|
+
* 补丁帧自己不带它(protocol 的 `type: 'diff'` 只有路径和两边内容),而它在流里
|
|
203
|
+
* 恒排在产出者那次调用的 `tool-result` **之前**,引擎又是**一次调用一次地**跑工具
|
|
204
|
+
* (`core/src/agent/loop.ts` 那个 for 循环)—— 于是收帧那一刻还没收口的那一次调用
|
|
205
|
+
* 就是它的产出者,`reducer` 据此补上。
|
|
206
|
+
*
|
|
207
|
+
* 有了这一格,宿主才能把补丁认回**那一张**卡;没有就只剩「同一路径的第 k 张」,
|
|
208
|
+
* 中间夹一个也带着这个路径、却不写文件的调用(读、或者名字认不出来的 MCP 工具)
|
|
209
|
+
* 时就认错一张 —— 补丁从自己那张卡后面跳到前一张后面。
|
|
210
|
+
*
|
|
211
|
+
* ⚠️ **只有实时流里补得出来**:落盘的补丁项没有这一格(库表里就没有),所以
|
|
212
|
+
* 渲染层必须容忍它缺席。
|
|
213
|
+
*/
|
|
214
|
+
callId?: string;
|
|
181
215
|
oldContent?: string;
|
|
182
216
|
newContent?: string;
|
|
183
217
|
/**
|
package/dist/index.js
CHANGED
|
@@ -30,6 +30,17 @@ function patchTool(pending, callId, patch) {
|
|
|
30
30
|
}
|
|
31
31
|
return null;
|
|
32
32
|
}
|
|
33
|
+
function openCallId(pending) {
|
|
34
|
+
for (let i = pending.length - 1; i >= 0; i--) {
|
|
35
|
+
const item = pending[i];
|
|
36
|
+
if (item?.type !== "tool_group") continue;
|
|
37
|
+
for (let j = item.tools.length - 1; j >= 0; j--) {
|
|
38
|
+
const tool = item.tools[j];
|
|
39
|
+
if (tool?.status === "pending") return tool.callId;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return void 0;
|
|
43
|
+
}
|
|
33
44
|
function applyStreamEvent(state, event, meta) {
|
|
34
45
|
const { pending, thought } = state;
|
|
35
46
|
const carry = (next) => state.sealed === void 0 || next.sealed !== void 0 ? next : { ...next, sealed: state.sealed };
|
|
@@ -88,7 +99,8 @@ function applyStreamEvent(state, event, meta) {
|
|
|
88
99
|
});
|
|
89
100
|
return next ? carry({ pending: next, thought }) : state;
|
|
90
101
|
}
|
|
91
|
-
case "diff":
|
|
102
|
+
case "diff": {
|
|
103
|
+
const producer = openCallId(pending);
|
|
92
104
|
return carry({
|
|
93
105
|
pending: [
|
|
94
106
|
...pending,
|
|
@@ -97,11 +109,13 @@ function applyStreamEvent(state, event, meta) {
|
|
|
97
109
|
filePath: event.filePath,
|
|
98
110
|
...event.oldContent !== void 0 ? { oldContent: event.oldContent } : {},
|
|
99
111
|
...event.newContent !== void 0 ? { newContent: event.newContent } : {},
|
|
112
|
+
...producer !== void 0 ? { callId: producer } : {},
|
|
100
113
|
...anchor(meta)
|
|
101
114
|
}
|
|
102
115
|
],
|
|
103
116
|
thought
|
|
104
117
|
});
|
|
118
|
+
}
|
|
105
119
|
case "notice":
|
|
106
120
|
return carry({
|
|
107
121
|
pending: [...pending, { type: "info", text: event.text, ...anchor(meta) }],
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@epoch-agent/view",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "6.1.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": "6.1.0"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"tsup": "^8.3.6",
|