@keyou007/agent-bot 0.1.20 → 0.1.21
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/CHANGELOG.md +10 -0
- package/README.md +15 -5
- package/README.zh.md +15 -5
- package/dist/codex/CodexEventMapper.js +21 -1
- package/dist/codex/CodexEventMapper.js.map +1 -1
- package/dist/codex/CodexProcessManager.js +3 -0
- package/dist/codex/CodexProcessManager.js.map +1 -1
- package/dist/codex/CodexRuntime.js +136 -4
- package/dist/codex/CodexRuntime.js.map +1 -1
- package/dist/codex/ProviderModels.js +159 -0
- package/dist/codex/ProviderModels.js.map +1 -0
- package/dist/commands/CommandRouter.js +8 -3
- package/dist/commands/CommandRouter.js.map +1 -1
- package/dist/console/ConsoleTurnPresenter.js +2 -1
- package/dist/console/ConsoleTurnPresenter.js.map +1 -1
- package/dist/feishu/CardRenderer.js +46 -16
- package/dist/feishu/CardRenderer.js.map +1 -1
- package/dist/feishu/FeishuTurnPresenter.js +34 -8
- package/dist/feishu/FeishuTurnPresenter.js.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/local-files/LocalFileViewerServer.js +151 -16
- package/dist/local-files/LocalFileViewerServer.js.map +1 -1
- package/dist/local-files/TurnPreviewPage.js +725 -0
- package/dist/local-files/TurnPreviewPage.js.map +1 -0
- package/dist/presentation/OutboundRouter.js +6 -1
- package/dist/presentation/OutboundRouter.js.map +1 -1
- package/dist/presentation/TurnStateReducer.js +142 -3
- package/dist/presentation/TurnStateReducer.js.map +1 -1
- package/dist/proxy/ProxySessionController.js +186 -80
- package/dist/proxy/ProxySessionController.js.map +1 -1
- package/dist/state/StateStore.js +7 -0
- package/dist/state/StateStore.js.map +1 -1
- package/dist/utils/formatStorageSize.js +15 -0
- package/dist/utils/formatStorageSize.js.map +1 -0
- package/docs/technical-reference.md +7 -1
- package/docs/technical-reference.zh.md +7 -1
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
- package/skills/agent-bot/SKILL.md +5 -1
- package/src/codex/CodexEventMapper.ts +31 -1
- package/src/codex/CodexProcessManager.ts +4 -0
- package/src/codex/CodexRuntime.ts +153 -9
- package/src/codex/ProviderModels.ts +185 -0
- package/src/commands/CommandRouter.ts +7 -2
- package/src/commands/commandTypes.ts +1 -1
- package/src/console/ConsoleTurnPresenter.ts +3 -0
- package/src/feishu/CardRenderer.ts +47 -16
- package/src/feishu/FeishuTurnPresenter.ts +46 -4
- package/src/index.ts +2 -0
- package/src/local-files/LocalFileViewerServer.ts +166 -17
- package/src/local-files/TurnPreviewPage.ts +792 -0
- package/src/presentation/OutboundRouter.ts +8 -0
- package/src/presentation/TurnStateReducer.ts +175 -2
- package/src/presentation/turnViewTypes.ts +13 -0
- package/src/proxy/ProxySessionController.ts +186 -67
- package/src/runtime/types.ts +18 -1
- package/src/state/StateStore.ts +9 -0
- package/src/utils/formatStorageSize.ts +13 -0
|
@@ -16,7 +16,9 @@ export interface TurnPresenter {
|
|
|
16
16
|
taskTitle?: string,
|
|
17
17
|
projectCwd?: string,
|
|
18
18
|
agentLabel?: string,
|
|
19
|
+
model?: string,
|
|
19
20
|
): void;
|
|
21
|
+
updateSessionModel(sessionId: string, model?: string): void;
|
|
20
22
|
updateSessionTitle(sessionId: string, taskTitle: string): void;
|
|
21
23
|
unregisterSession(sessionId: string): void;
|
|
22
24
|
startPendingTurn(
|
|
@@ -67,11 +69,13 @@ export class OutboundRouter {
|
|
|
67
69
|
taskTitle?: string,
|
|
68
70
|
projectCwd?: string,
|
|
69
71
|
agentLabel?: string,
|
|
72
|
+
model?: string,
|
|
70
73
|
): void {
|
|
71
74
|
const route = this.route(contextKey);
|
|
72
75
|
this.sessionRoutes.set(sessionId, route);
|
|
73
76
|
this.sessionContextKeys.set(sessionId, contextKey);
|
|
74
77
|
route.presenter.registerSession(sessionId, contextKey, taskTitle, projectCwd, agentLabel);
|
|
78
|
+
if (model) route.presenter.updateSessionModel(sessionId, model);
|
|
75
79
|
}
|
|
76
80
|
|
|
77
81
|
getSessionContextKey(sessionId: string): string | undefined {
|
|
@@ -90,6 +94,10 @@ export class OutboundRouter {
|
|
|
90
94
|
this.sessionRoutes.get(sessionId)?.presenter.updateSessionTitle(sessionId, taskTitle);
|
|
91
95
|
}
|
|
92
96
|
|
|
97
|
+
updateSessionModel(sessionId: string, model?: string): void {
|
|
98
|
+
this.sessionRoutes.get(sessionId)?.presenter.updateSessionModel(sessionId, model);
|
|
99
|
+
}
|
|
100
|
+
|
|
93
101
|
unregisterSession(sessionId: string): void {
|
|
94
102
|
const route = this.sessionRoutes.get(sessionId);
|
|
95
103
|
route?.presenter.unregisterSession(sessionId);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { AgentEvent, ToolState } from "../runtime/types.js";
|
|
2
2
|
import { appendGeneratedImageMarkdown } from "../utils/generatedImageMarkdown.js";
|
|
3
3
|
import type { MessageReplyTarget } from "../feishu/types.js";
|
|
4
|
+
import { formatStorageSize } from "../utils/formatStorageSize.js";
|
|
4
5
|
import type { FileSummary, TurnActivity, TurnViewState } from "./turnViewTypes.js";
|
|
5
6
|
|
|
6
7
|
const MAX_TEXT = 6_000;
|
|
@@ -17,11 +18,13 @@ export function createTurnViewState(
|
|
|
17
18
|
projectCwd?: string,
|
|
18
19
|
prompt?: string,
|
|
19
20
|
agentLabel?: string,
|
|
21
|
+
model?: string,
|
|
20
22
|
): TurnViewState {
|
|
21
23
|
return {
|
|
22
24
|
sessionId,
|
|
23
25
|
turnId,
|
|
24
26
|
agentLabel,
|
|
27
|
+
model,
|
|
25
28
|
taskTitle,
|
|
26
29
|
prompt,
|
|
27
30
|
projectCwd,
|
|
@@ -31,6 +34,8 @@ export function createTurnViewState(
|
|
|
31
34
|
assistantText: "",
|
|
32
35
|
plan: [],
|
|
33
36
|
activities: [],
|
|
37
|
+
fullToolOutputs: {},
|
|
38
|
+
fullToolErrors: {},
|
|
34
39
|
totalToolCount: 0,
|
|
35
40
|
completedToolCount: 0,
|
|
36
41
|
failedToolCount: 0,
|
|
@@ -60,6 +65,93 @@ export function reduceTurnEvent(state: TurnViewState, event: AgentEvent): TurnVi
|
|
|
60
65
|
...state,
|
|
61
66
|
totalTokens: (state.totalTokens ?? 0) + delta,
|
|
62
67
|
tokenUsageCumulative: Math.max(previousCumulative ?? 0, cumulativeTokens),
|
|
68
|
+
latestContextTokens: event.contextTokens === undefined
|
|
69
|
+
? state.latestContextTokens
|
|
70
|
+
: normalizeTokenCount(event.contextTokens),
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
case "context_compaction": {
|
|
74
|
+
if (event.phase === "started") {
|
|
75
|
+
const compactionId = event.compactionId ?? `generated:${(state.contextCompactionCount ?? 0) + 1}`;
|
|
76
|
+
const beforeTokens = state.latestContextTokens;
|
|
77
|
+
const text = contextCompactionRunningText(beforeTokens, event.turnCount, event.storageBytes);
|
|
78
|
+
const activityId = `context-compaction:${compactionId}`;
|
|
79
|
+
const activityUpdate = upsertActivity(
|
|
80
|
+
state.activities ?? [],
|
|
81
|
+
(state.activities ?? []).findIndex((activity) => activity.id === activityId),
|
|
82
|
+
{ kind: "assistant", id: activityId, text },
|
|
83
|
+
);
|
|
84
|
+
return {
|
|
85
|
+
...state,
|
|
86
|
+
contextCompactionStatus: "running",
|
|
87
|
+
contextCompactionId: compactionId,
|
|
88
|
+
contextCompactionStartedAt: event.timestampMs ?? Date.now(),
|
|
89
|
+
contextCompactionDurationMs: undefined,
|
|
90
|
+
contextCompactionBeforeTokens: beforeTokens,
|
|
91
|
+
contextCompactionAfterTokens: undefined,
|
|
92
|
+
contextCompactionTurnCount: event.turnCount,
|
|
93
|
+
contextCompactionStorageBytes: event.storageBytes,
|
|
94
|
+
progressText: text,
|
|
95
|
+
activities: activityUpdate.activities,
|
|
96
|
+
activitiesTruncated: state.activitiesTruncated || activityUpdate.truncated,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
const currentId = state.contextCompactionId;
|
|
100
|
+
const repeatedItemCompletion = event.compactionId !== undefined
|
|
101
|
+
&& event.compactionId === currentId
|
|
102
|
+
&& state.contextCompactionStatus === "completed";
|
|
103
|
+
const repeatedLegacyCompletion = event.compactionId === undefined
|
|
104
|
+
&& state.contextCompactionStatus === "completed";
|
|
105
|
+
const repeatedCompletion = repeatedItemCompletion || repeatedLegacyCompletion;
|
|
106
|
+
const contextCompactionCount = repeatedCompletion
|
|
107
|
+
? state.contextCompactionCount ?? 1
|
|
108
|
+
: (state.contextCompactionCount ?? 0) + 1;
|
|
109
|
+
const compactionId = event.compactionId
|
|
110
|
+
?? currentId
|
|
111
|
+
?? `legacy:${contextCompactionCount}`;
|
|
112
|
+
const completedAt = event.timestampMs ?? Date.now();
|
|
113
|
+
const durationMs = repeatedCompletion
|
|
114
|
+
? state.contextCompactionDurationMs
|
|
115
|
+
: state.contextCompactionStartedAt === undefined
|
|
116
|
+
? undefined
|
|
117
|
+
: Math.max(0, completedAt - state.contextCompactionStartedAt);
|
|
118
|
+
const beforeTokens = state.contextCompactionBeforeTokens;
|
|
119
|
+
const afterTokens = repeatedCompletion
|
|
120
|
+
? state.contextCompactionAfterTokens
|
|
121
|
+
: state.latestContextTokens;
|
|
122
|
+
const turnCount = repeatedCompletion
|
|
123
|
+
? state.contextCompactionTurnCount
|
|
124
|
+
: event.turnCount ?? state.contextCompactionTurnCount;
|
|
125
|
+
const storageBytes = repeatedCompletion
|
|
126
|
+
? state.contextCompactionStorageBytes
|
|
127
|
+
: event.storageBytes ?? state.contextCompactionStorageBytes;
|
|
128
|
+
const text = contextCompactionCompletedText(
|
|
129
|
+
contextCompactionCount,
|
|
130
|
+
durationMs,
|
|
131
|
+
beforeTokens,
|
|
132
|
+
afterTokens,
|
|
133
|
+
turnCount,
|
|
134
|
+
storageBytes,
|
|
135
|
+
);
|
|
136
|
+
const activityId = `context-compaction:${compactionId}`;
|
|
137
|
+
const activityUpdate = upsertActivity(
|
|
138
|
+
state.activities ?? [],
|
|
139
|
+
(state.activities ?? []).findIndex((activity) => activity.id === activityId),
|
|
140
|
+
{ kind: "assistant", id: activityId, text },
|
|
141
|
+
);
|
|
142
|
+
return {
|
|
143
|
+
...state,
|
|
144
|
+
contextCompactionStatus: "completed",
|
|
145
|
+
contextCompactionCount,
|
|
146
|
+
contextCompactionId: compactionId,
|
|
147
|
+
contextCompactionDurationMs: durationMs,
|
|
148
|
+
contextCompactionBeforeTokens: beforeTokens,
|
|
149
|
+
contextCompactionAfterTokens: afterTokens,
|
|
150
|
+
contextCompactionTurnCount: turnCount,
|
|
151
|
+
contextCompactionStorageBytes: storageBytes,
|
|
152
|
+
progressText: text,
|
|
153
|
+
activities: activityUpdate.activities,
|
|
154
|
+
activitiesTruncated: state.activitiesTruncated || activityUpdate.truncated,
|
|
63
155
|
};
|
|
64
156
|
}
|
|
65
157
|
case "progress": {
|
|
@@ -165,10 +257,11 @@ function reduceToolOutputDelta(state: TurnViewState, toolId: string, delta: stri
|
|
|
165
257
|
if (!delta) return state;
|
|
166
258
|
const tool = findTool(state, toolId);
|
|
167
259
|
if (!tool || tool.status !== "running" || tool.kind !== "command") return state;
|
|
260
|
+
const fullOutput = `${state.fullToolOutputs?.[toolId] ?? tool.output ?? ""}${delta}`;
|
|
168
261
|
return reduceToolUpdate(state, {
|
|
169
262
|
...tool,
|
|
170
263
|
output: appendBoundedOutput(tool.output, delta),
|
|
171
|
-
});
|
|
264
|
+
}, fullOutput);
|
|
172
265
|
}
|
|
173
266
|
|
|
174
267
|
function findTool(state: TurnViewState, toolId: string): ToolState | undefined {
|
|
@@ -197,8 +290,15 @@ function appendBoundedOutput(previous: string | undefined, delta: string): strin
|
|
|
197
290
|
return `…${combined.slice(-(MAX_TEXT - 1))}`;
|
|
198
291
|
}
|
|
199
292
|
|
|
200
|
-
function reduceToolUpdate(state: TurnViewState, tool: ToolState): TurnViewState {
|
|
293
|
+
function reduceToolUpdate(state: TurnViewState, tool: ToolState, fullOutput?: string): TurnViewState {
|
|
201
294
|
tool = withToolTiming(state, tool);
|
|
295
|
+
const nextFullOutput = fullOutput ?? tool.output;
|
|
296
|
+
const fullToolOutputs = nextFullOutput === undefined
|
|
297
|
+
? state.fullToolOutputs
|
|
298
|
+
: { ...state.fullToolOutputs, [tool.id]: nextFullOutput };
|
|
299
|
+
const fullToolErrors = tool.error === undefined
|
|
300
|
+
? state.fullToolErrors
|
|
301
|
+
: { ...state.fullToolErrors, [tool.id]: tool.error };
|
|
202
302
|
const tracking = trackToolStatus(state, tool);
|
|
203
303
|
const bounded = boundTool(tool);
|
|
204
304
|
const activityUpdate = upsertToolActivity(state.activities ?? [], bounded);
|
|
@@ -210,6 +310,8 @@ function reduceToolUpdate(state: TurnViewState, tool: ToolState): TurnViewState
|
|
|
210
310
|
return {
|
|
211
311
|
...state,
|
|
212
312
|
...tracking,
|
|
313
|
+
fullToolOutputs,
|
|
314
|
+
fullToolErrors,
|
|
213
315
|
status: "tool_running",
|
|
214
316
|
activeTool: bounded,
|
|
215
317
|
activities: activityUpdate.activities,
|
|
@@ -224,6 +326,8 @@ function reduceToolUpdate(state: TurnViewState, tool: ToolState): TurnViewState
|
|
|
224
326
|
return {
|
|
225
327
|
...state,
|
|
226
328
|
...tracking,
|
|
329
|
+
fullToolOutputs,
|
|
330
|
+
fullToolErrors,
|
|
227
331
|
status: activeTool ? "tool_running" : "running",
|
|
228
332
|
activeTool,
|
|
229
333
|
activities: activityUpdate.activities,
|
|
@@ -237,6 +341,8 @@ function reduceToolUpdate(state: TurnViewState, tool: ToolState): TurnViewState
|
|
|
237
341
|
return {
|
|
238
342
|
...state,
|
|
239
343
|
...tracking,
|
|
344
|
+
fullToolOutputs,
|
|
345
|
+
fullToolErrors,
|
|
240
346
|
status: activeTool ? "tool_running" : "running",
|
|
241
347
|
activeTool,
|
|
242
348
|
activities: activityUpdate.activities,
|
|
@@ -360,3 +466,70 @@ function bound(value: string): string {
|
|
|
360
466
|
function normalizeTokenCount(value: number): number {
|
|
361
467
|
return Math.max(0, Math.round(value));
|
|
362
468
|
}
|
|
469
|
+
|
|
470
|
+
function contextCompactionCompletedText(
|
|
471
|
+
count: number,
|
|
472
|
+
durationMs: number | undefined,
|
|
473
|
+
beforeTokens: number | undefined,
|
|
474
|
+
afterTokens: number | undefined,
|
|
475
|
+
turnCount: number | undefined,
|
|
476
|
+
storageBytes: number | undefined,
|
|
477
|
+
): string {
|
|
478
|
+
const subject = count > 1
|
|
479
|
+
? `Codex 已完成本轮第 ${count} 次上下文压缩`
|
|
480
|
+
: "Codex 已完成上下文压缩";
|
|
481
|
+
const details = [
|
|
482
|
+
durationMs === undefined ? undefined : `耗时 ${formatCompactionDuration(durationMs)}`,
|
|
483
|
+
contextCompactionSizeText(beforeTokens, afterTokens),
|
|
484
|
+
contextCompactionTaskMetricsText(turnCount, storageBytes),
|
|
485
|
+
].filter((detail): detail is string => detail !== undefined);
|
|
486
|
+
return `${subject}${details.length > 0 ? ` · ${details.join(" · ")}` : ""},继续处理。`;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
function contextCompactionRunningText(
|
|
490
|
+
beforeTokens: number | undefined,
|
|
491
|
+
turnCount: number | undefined,
|
|
492
|
+
storageBytes: number | undefined,
|
|
493
|
+
): string {
|
|
494
|
+
const context = beforeTokens === undefined
|
|
495
|
+
? "Codex 正在压缩上下文…"
|
|
496
|
+
: `Codex 正在压缩上下文(压缩前 ${formatInteger(beforeTokens)} tokens)…`;
|
|
497
|
+
const metrics = contextCompactionTaskMetricsText(turnCount, storageBytes);
|
|
498
|
+
return metrics ? `${context} · ${metrics}` : context;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
function contextCompactionTaskMetricsText(
|
|
502
|
+
turnCount: number | undefined,
|
|
503
|
+
storageBytes: number | undefined,
|
|
504
|
+
): string {
|
|
505
|
+
const turns = turnCount === undefined ? "已执行轮次未知" : `已执行 ${formatInteger(turnCount)} 轮`;
|
|
506
|
+
const storage = storageBytes === undefined ? "磁盘占用未知" : `磁盘占用 ${formatStorageSize(storageBytes)}`;
|
|
507
|
+
return `${turns} · ${storage}`;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
function contextCompactionSizeText(
|
|
511
|
+
beforeTokens: number | undefined,
|
|
512
|
+
afterTokens: number | undefined,
|
|
513
|
+
): string | undefined {
|
|
514
|
+
if (afterTokens === undefined) return undefined;
|
|
515
|
+
if (beforeTokens === undefined) return `压缩后 ${formatInteger(afterTokens)} tokens`;
|
|
516
|
+
if (afterTokens >= beforeTokens || beforeTokens === 0) return undefined;
|
|
517
|
+
const reduction = Math.round((1 - afterTokens / beforeTokens) * 100);
|
|
518
|
+
return `上下文 ${formatInteger(beforeTokens)} → ${formatInteger(afterTokens)} tokens(减少 ${reduction}%)`;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
function formatCompactionDuration(durationMs: number): string {
|
|
522
|
+
const seconds = Math.max(0, durationMs) / 1_000;
|
|
523
|
+
if (seconds < 10) return `${Number(seconds.toFixed(1))}s`;
|
|
524
|
+
if (seconds < 60) return `${Math.round(seconds)}s`;
|
|
525
|
+
const totalSeconds = Math.round(seconds);
|
|
526
|
+
const hours = Math.floor(totalSeconds / 3_600);
|
|
527
|
+
const minutes = Math.floor((totalSeconds % 3_600) / 60);
|
|
528
|
+
const remainingSeconds = totalSeconds % 60;
|
|
529
|
+
const time = `${String(minutes).padStart(2, "0")}:${String(remainingSeconds).padStart(2, "0")}`;
|
|
530
|
+
return hours > 0 ? `${String(hours).padStart(2, "0")}:${time}` : time;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
function formatInteger(value: number): string {
|
|
534
|
+
return normalizeTokenCount(value).toLocaleString("en-US");
|
|
535
|
+
}
|
|
@@ -26,6 +26,7 @@ export interface TurnViewState {
|
|
|
26
26
|
sessionId: string;
|
|
27
27
|
turnId: string;
|
|
28
28
|
agentLabel?: string;
|
|
29
|
+
model?: string;
|
|
29
30
|
taskTitle?: string;
|
|
30
31
|
prompt?: string;
|
|
31
32
|
projectCwd?: string;
|
|
@@ -36,10 +37,22 @@ export interface TurnViewState {
|
|
|
36
37
|
durationMs?: number;
|
|
37
38
|
totalTokens?: number;
|
|
38
39
|
tokenUsageCumulative?: number;
|
|
40
|
+
contextCompactionStatus?: "running" | "completed";
|
|
41
|
+
contextCompactionCount?: number;
|
|
42
|
+
contextCompactionId?: string;
|
|
43
|
+
latestContextTokens?: number;
|
|
44
|
+
contextCompactionStartedAt?: number;
|
|
45
|
+
contextCompactionDurationMs?: number;
|
|
46
|
+
contextCompactionBeforeTokens?: number;
|
|
47
|
+
contextCompactionAfterTokens?: number;
|
|
48
|
+
contextCompactionTurnCount?: number;
|
|
49
|
+
contextCompactionStorageBytes?: number;
|
|
39
50
|
progressText?: string;
|
|
40
51
|
assistantText: string;
|
|
41
52
|
plan: PlanStep[];
|
|
42
53
|
activities: TurnActivity[];
|
|
54
|
+
fullToolOutputs?: Record<string, string>;
|
|
55
|
+
fullToolErrors?: Record<string, string>;
|
|
43
56
|
activitiesTruncated?: boolean;
|
|
44
57
|
totalToolCount?: number;
|
|
45
58
|
completedToolCount?: number;
|