@armadra/agent 0.7.2 → 0.7.3
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 +4 -0
- package/CHANGELOG.zh-CN.md +4 -0
- package/dist/agent/session-compaction.d.ts +6 -0
- package/dist/agent/session-compaction.js +15 -9
- package/dist/bundle/ama.cjs +33 -13
- package/dist/compaction/prefix-estimate.d.ts +3 -1
- package/dist/compaction/prefix-estimate.js +7 -3
- package/dist/i18n/catalog.d.ts +2 -0
- package/dist/i18n/messages/report-context.d.ts +4 -0
- package/dist/i18n/messages/report-context.js +4 -0
- package/dist/i18n/messages/report.d.ts +4 -0
- package/dist/modes/context-report.d.ts +2 -0
- package/dist/modes/context-report.js +23 -7
- package/docs/en/tui.md +1 -1
- package/docs/tui.md +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,10 @@ English · [简体中文](CHANGELOG.zh-CN.md)
|
|
|
5
5
|
> This file is in English starting with 0.6.0. Release notes for 0.1 through 0.5.1 are in Chinese in
|
|
6
6
|
> [CHANGELOG.zh-CN.md](CHANGELOG.zh-CN.md). New entries go into both files.
|
|
7
7
|
|
|
8
|
+
## 0.7.3 (2026-10-09)
|
|
9
|
+
|
|
10
|
+
- **`/context` before the first request**: it showed `0 / <window>` and left the system prompt and tool declarations out while the status bar already showed the baseline; it now estimates both from what the first request will send (shared with the status-bar baseline), and "Used" always matches the status bar and `getStats()`.
|
|
11
|
+
|
|
8
12
|
## 0.7.2 (2026-10-09)
|
|
9
13
|
|
|
10
14
|
- **Prompt cache survives mid-session context changes**: when AGENTS.md, Skills or SessionStart hook output changed on
|
package/CHANGELOG.zh-CN.md
CHANGED
|
@@ -4,6 +4,10 @@
|
|
|
4
4
|
|
|
5
5
|
> 从 0.6.0 起 [CHANGELOG.md](CHANGELOG.md) 为英文,本文件保留中文记录(0.1–0.5.1 的完整历史在此)。新条目两份都要加。
|
|
6
6
|
|
|
7
|
+
## 0.7.3(2026-10-09)
|
|
8
|
+
|
|
9
|
+
- **首次请求前的 `/context`**:状态栏已显示启动基线时,它仍是 `0 / <窗口>`、不计系统提示与工具声明;现在按首次请求将要发送的内容估算这两类(与状态栏基线共用同一份装配),「已用」始终与状态栏和 `getStats()` 一致。
|
|
10
|
+
|
|
7
11
|
## 0.7.2(2026-10-09)
|
|
8
12
|
|
|
9
13
|
- **会话中途上下文变化不再打断提示缓存**:resume 时 AGENTS.md、Skills 或 SessionStart Hook 输出变了、宿主 instructions 刷新、压缩后记忆节
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
*/
|
|
22
22
|
import { CompactionBreaker } from "../compaction/breaker.js";
|
|
23
23
|
import { type ContextEstimate } from "../compaction/estimate.js";
|
|
24
|
+
import type { SystemMessage } from "../ai/types.js";
|
|
24
25
|
import { type PrunePolicy } from "../compaction/prune-tier.js";
|
|
25
26
|
import type { BranchSummaryEntry } from "../session/types.js";
|
|
26
27
|
import type { SessionCore } from "./session-core.js";
|
|
@@ -88,3 +89,8 @@ export declare class CompactionController {
|
|
|
88
89
|
leaveBranch(targetId: string | null, instructions: string | undefined, signal: AbortSignal): Promise<BranchSummaryEntry | undefined>;
|
|
89
90
|
private summarize;
|
|
90
91
|
}
|
|
92
|
+
/**
|
|
93
|
+
* 首次请求前将要发送的首条 `system` 消息(系统提示 + 工具声明,与真正发送时同样装配)。只用于统计:
|
|
94
|
+
* `getStats` 的启动基线与 `/context` 的分类明细共用它,口径一致;不写分支、不改请求体。
|
|
95
|
+
*/
|
|
96
|
+
export declare function pendingPrefixMessage(core: SessionCore): SystemMessage;
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
import { AmaError } from "../errors.js";
|
|
23
23
|
import { CompactionBreaker } from "../compaction/breaker.js";
|
|
24
24
|
import { estimateMessageTokens, estimateProjectedTokens, } from "../compaction/estimate.js";
|
|
25
|
-
import {
|
|
25
|
+
import { prefixSystemMessage, withPrefixBaseline } from "../compaction/prefix-estimate.js";
|
|
26
26
|
import { planPrune, prunePolicy } from "../compaction/prune-tier.js";
|
|
27
27
|
import { createProtection, skillLocations } from "../compaction/protect.js";
|
|
28
28
|
import { buildPostCompactBlock } from "../compaction/post-compact.js";
|
|
@@ -96,14 +96,7 @@ export class CompactionController {
|
|
|
96
96
|
}
|
|
97
97
|
/** 将要发送的系统提示 + 工具声明的估算(与首条 system 消息同样装配,不落盘)。 */
|
|
98
98
|
prefixTokens() {
|
|
99
|
-
|
|
100
|
-
const tools = core
|
|
101
|
-
.activeToolNames()
|
|
102
|
-
.map((name) => core.activeTool(name))
|
|
103
|
-
.filter((tool) => tool !== undefined);
|
|
104
|
-
const system = core.childBase?.().system ?? core.options.system ?? {};
|
|
105
|
-
const sections = definedSections(assembleSections({ ...system, tools, cwd: core.cwd }));
|
|
106
|
-
return estimatePrefixTokens(sections, toolDecls(tools));
|
|
99
|
+
return estimateMessageTokens(pendingPrefixMessage(this.core));
|
|
107
100
|
}
|
|
108
101
|
budget() {
|
|
109
102
|
const window = this.core.model().contextWindow;
|
|
@@ -411,3 +404,16 @@ export class CompactionController {
|
|
|
411
404
|
}
|
|
412
405
|
}
|
|
413
406
|
}
|
|
407
|
+
/**
|
|
408
|
+
* 首次请求前将要发送的首条 `system` 消息(系统提示 + 工具声明,与真正发送时同样装配)。只用于统计:
|
|
409
|
+
* `getStats` 的启动基线与 `/context` 的分类明细共用它,口径一致;不写分支、不改请求体。
|
|
410
|
+
*/
|
|
411
|
+
export function pendingPrefixMessage(core) {
|
|
412
|
+
const tools = core
|
|
413
|
+
.activeToolNames()
|
|
414
|
+
.map((name) => core.activeTool(name))
|
|
415
|
+
.filter((tool) => tool !== undefined);
|
|
416
|
+
const system = core.childBase?.().system ?? core.options.system ?? {};
|
|
417
|
+
const sections = definedSections(assembleSections({ ...system, tools, cwd: core.cwd }));
|
|
418
|
+
return prefixSystemMessage(sections, toolDecls(tools));
|
|
419
|
+
}
|
package/dist/bundle/ama.cjs
CHANGED
|
@@ -41,7 +41,7 @@ var AMA_VERSION;
|
|
|
41
41
|
var init_version = __esm({
|
|
42
42
|
"src/version.ts"() {
|
|
43
43
|
"use strict";
|
|
44
|
-
AMA_VERSION = true ? "0.7.
|
|
44
|
+
AMA_VERSION = true ? "0.7.3" : readPackageVersion();
|
|
45
45
|
}
|
|
46
46
|
});
|
|
47
47
|
|
|
@@ -2992,6 +2992,7 @@ var init_report_context = __esm({
|
|
|
2992
2992
|
keySource: "Source",
|
|
2993
2993
|
sourceUsage: (usage, estimate) => `reported usage ${usage} + estimated ${estimate}`,
|
|
2994
2994
|
sourceEstimate: "estimated in full (no usable usage yet)",
|
|
2995
|
+
sourcePrefix: "system prompt and tools only, estimated before the first request",
|
|
2995
2996
|
keyAutoCompact: "Auto-compact",
|
|
2996
2997
|
autoCompact: (at, distance2) => `at ${at}, ≈ ${distance2} to go`,
|
|
2997
2998
|
autoCompactReached: (at) => `at ${at}, reached`,
|
|
@@ -3002,6 +3003,7 @@ var init_report_context = __esm({
|
|
|
3002
3003
|
breakdown: (total) => `By category (estimated, ${total})`,
|
|
3003
3004
|
prefixCounted: "System prompt and tool declarations are counted from the stored system messages; the full estimate can differ from reported usage.",
|
|
3004
3005
|
prefixPending: "System prompt and tool declarations are not counted until the first request.",
|
|
3006
|
+
prefixEstimated: "System prompt and tool declarations are estimated from what the first request will send.",
|
|
3005
3007
|
categories: {
|
|
3006
3008
|
system: "System prompt",
|
|
3007
3009
|
tools: "Tool declarations",
|
|
@@ -3036,6 +3038,7 @@ var init_report_context = __esm({
|
|
|
3036
3038
|
keySource: "来源",
|
|
3037
3039
|
sourceUsage: (usage, estimate) => `usage 实测 ${usage} + 估算 ${estimate}`,
|
|
3038
3040
|
sourceEstimate: "全量估算(还没有可用的 usage)",
|
|
3041
|
+
sourcePrefix: "首次请求前,只有系统提示与工具声明的估算",
|
|
3039
3042
|
keyAutoCompact: "自动压缩",
|
|
3040
3043
|
autoCompact: (at, distance2) => `${at} 触发,还差 ≈ ${distance2}`,
|
|
3041
3044
|
autoCompactReached: (at) => `${at} 触发,已达到`,
|
|
@@ -3046,6 +3049,7 @@ var init_report_context = __esm({
|
|
|
3046
3049
|
breakdown: (total) => `按类别(估算,${total})`,
|
|
3047
3050
|
prefixCounted: "系统提示与工具声明按会话里存的 system 消息计入;全量估算可能与 usage 实测不同。",
|
|
3048
3051
|
prefixPending: "系统提示与工具声明在首次请求后才计入。",
|
|
3052
|
+
prefixEstimated: "系统提示与工具声明按首次请求将要发送的内容估算。",
|
|
3049
3053
|
categories: {
|
|
3050
3054
|
system: "系统提示",
|
|
3051
3055
|
tools: "工具声明",
|
|
@@ -22051,10 +22055,10 @@ var init_breaker = __esm({
|
|
|
22051
22055
|
});
|
|
22052
22056
|
|
|
22053
22057
|
// src/compaction/prefix-estimate.ts
|
|
22054
|
-
function
|
|
22058
|
+
function prefixSystemMessage(sections, tools) {
|
|
22055
22059
|
const message = { role: "system", sections: { ...sections }, timestamp: 0 };
|
|
22056
22060
|
if (tools.length > 0) message.toolsAdded = [...tools];
|
|
22057
|
-
return
|
|
22061
|
+
return message;
|
|
22058
22062
|
}
|
|
22059
22063
|
function withPrefixBaseline(estimate, hasSystemMessage, prefix) {
|
|
22060
22064
|
if (estimate.usageTokens > 0) return { ...estimate, source: "usage" };
|
|
@@ -22070,7 +22074,6 @@ function withPrefixBaseline(estimate, hasSystemMessage, prefix) {
|
|
|
22070
22074
|
var init_prefix_estimate = __esm({
|
|
22071
22075
|
"src/compaction/prefix-estimate.ts"() {
|
|
22072
22076
|
"use strict";
|
|
22073
|
-
init_estimate();
|
|
22074
22077
|
}
|
|
22075
22078
|
});
|
|
22076
22079
|
|
|
@@ -22403,6 +22406,12 @@ var init_system_prompt = __esm({
|
|
|
22403
22406
|
});
|
|
22404
22407
|
|
|
22405
22408
|
// src/agent/session-compaction.ts
|
|
22409
|
+
function pendingPrefixMessage(core) {
|
|
22410
|
+
const tools = core.activeToolNames().map((name) => core.activeTool(name)).filter((tool) => tool !== void 0);
|
|
22411
|
+
const system = core.childBase?.().system ?? core.options.system ?? {};
|
|
22412
|
+
const sections = definedSections(assembleSections({ ...system, tools, cwd: core.cwd }));
|
|
22413
|
+
return prefixSystemMessage(sections, toolDecls(tools));
|
|
22414
|
+
}
|
|
22406
22415
|
var DEFAULT_COMPACTION_SETTINGS, KEEP_RECENT_MAX_RATIO, HOOK_CONTEXT_CUSTOM_TYPE, CompactionController;
|
|
22407
22416
|
var init_session_compaction = __esm({
|
|
22408
22417
|
"src/agent/session-compaction.ts"() {
|
|
@@ -22484,11 +22493,7 @@ var init_session_compaction = __esm({
|
|
|
22484
22493
|
}
|
|
22485
22494
|
/** 将要发送的系统提示 + 工具声明的估算(与首条 system 消息同样装配,不落盘)。 */
|
|
22486
22495
|
prefixTokens() {
|
|
22487
|
-
|
|
22488
|
-
const tools = core.activeToolNames().map((name) => core.activeTool(name)).filter((tool) => tool !== void 0);
|
|
22489
|
-
const system = core.childBase?.().system ?? core.options.system ?? {};
|
|
22490
|
-
const sections = definedSections(assembleSections({ ...system, tools, cwd: core.cwd }));
|
|
22491
|
-
return estimatePrefixTokens(sections, toolDecls(tools));
|
|
22496
|
+
return estimateMessageTokens(pendingPrefixMessage(this.core));
|
|
22492
22497
|
}
|
|
22493
22498
|
budget() {
|
|
22494
22499
|
const window2 = this.core.model().contextWindow;
|
|
@@ -52549,20 +52554,28 @@ function contextSnapshot(session) {
|
|
|
52549
52554
|
let messages = session.messages;
|
|
52550
52555
|
let estimate;
|
|
52551
52556
|
let compaction = {};
|
|
52557
|
+
let prefixPending = false;
|
|
52552
52558
|
if (session instanceof AgentSessionImpl) {
|
|
52553
52559
|
const branch = session.manager.branch();
|
|
52554
52560
|
const items = buildProjection(branch).items;
|
|
52555
52561
|
messages = items.map((item) => item.message);
|
|
52556
52562
|
estimate = estimateProjectedTokens(items, branch);
|
|
52557
52563
|
compaction = session.options.compaction ?? {};
|
|
52564
|
+
if (!messages.some((message) => "role" in message && message.role === "system")) {
|
|
52565
|
+
prefixPending = true;
|
|
52566
|
+
messages = [pendingPrefixMessage(session), ...messages];
|
|
52567
|
+
}
|
|
52558
52568
|
} else estimate = estimateContextTokens(messages);
|
|
52559
|
-
const
|
|
52569
|
+
const stats = session.getStats();
|
|
52570
|
+
if (stats.contextTokens !== void 0) estimate = { ...estimate, tokens: stats.contextTokens };
|
|
52571
|
+
const window2 = stats.contextWindow;
|
|
52560
52572
|
const reserve = compaction.reserveTokens ?? DEFAULT_COMPACTION_SETTINGS.reserveTokens;
|
|
52561
52573
|
const budget = window2 === void 0 ? void 0 : Math.max(0, window2 - reserve);
|
|
52562
52574
|
const percent3 = window2 === void 0 || window2 <= 0 ? void 0 : Math.min(100, Math.round(estimate.tokens / window2 * 1e3) / 10);
|
|
52563
52575
|
return {
|
|
52564
52576
|
breakdown: breakdownMessages(messages),
|
|
52565
52577
|
estimate,
|
|
52578
|
+
prefixPending,
|
|
52566
52579
|
window: window2,
|
|
52567
52580
|
percent: percent3,
|
|
52568
52581
|
autoCompaction: session.state.autoCompaction,
|
|
@@ -52590,7 +52603,7 @@ function headerRows(snap) {
|
|
|
52590
52603
|
});
|
|
52591
52604
|
rows.push({
|
|
52592
52605
|
key: m.keySource,
|
|
52593
|
-
value: estimate.lastUsageIndex === null ? m.sourceEstimate : m.sourceUsage(
|
|
52606
|
+
value: snap.prefixPending ? m.sourcePrefix : estimate.lastUsageIndex === null ? m.sourceEstimate : m.sourceUsage(
|
|
52594
52607
|
formatTokenCount(estimate.usageTokens),
|
|
52595
52608
|
formatTokenCount(estimate.trailingTokens)
|
|
52596
52609
|
)
|
|
@@ -52648,7 +52661,9 @@ function describeContext(session) {
|
|
|
52648
52661
|
const { breakdown } = snap;
|
|
52649
52662
|
const lines2 = [m.title, ...renderRows(headerRows(snap), " "), ""];
|
|
52650
52663
|
lines2.push(m.breakdown(formatTokenCount(breakdown.total)));
|
|
52651
|
-
lines2.push(
|
|
52664
|
+
lines2.push(
|
|
52665
|
+
` ${snap.prefixPending ? m.prefixEstimated : breakdown.hasSystem ? m.prefixCounted : m.prefixPending}`
|
|
52666
|
+
);
|
|
52652
52667
|
if (breakdown.total === 0) lines2.push(` ${m.empty}`);
|
|
52653
52668
|
else lines2.push(...renderRows(categoryRows(breakdown, void 0), " "));
|
|
52654
52669
|
lines2.push("", m.largest(CONTEXT_TOP_RESULTS));
|
|
@@ -52669,7 +52684,12 @@ function contextPanel(session, theme) {
|
|
|
52669
52684
|
theme.bold(m.breakdown(formatTokenCount(breakdown.total))),
|
|
52670
52685
|
new Indent(
|
|
52671
52686
|
new Stack([
|
|
52672
|
-
new Text(
|
|
52687
|
+
new Text(
|
|
52688
|
+
theme.fg(
|
|
52689
|
+
"dim",
|
|
52690
|
+
snap.prefixPending ? m.prefixEstimated : breakdown.hasSystem ? m.prefixCounted : m.prefixPending
|
|
52691
|
+
)
|
|
52692
|
+
),
|
|
52673
52693
|
breakdown.total === 0 ? theme.fg("dim", m.empty) : table2(categoryRows(breakdown, theme), true)
|
|
52674
52694
|
]),
|
|
52675
52695
|
2
|
|
@@ -7,8 +7,10 @@
|
|
|
7
7
|
*
|
|
8
8
|
* 只用于统计:不写分支、不改请求体,前缀缓存不受影响;压缩阈值也不看它(摘要压不掉这部分)。
|
|
9
9
|
*/
|
|
10
|
-
import type { ToolDecl } from "../ai/types.js";
|
|
10
|
+
import type { SystemMessage, ToolDecl } from "../ai/types.js";
|
|
11
11
|
import type { ContextEstimate } from "./estimate.js";
|
|
12
|
+
/** 将要作为首条 `system` 消息发送的内容(只用于统计,不落盘)。 */
|
|
13
|
+
export declare function prefixSystemMessage(sections: Readonly<Record<string, string>>, tools: readonly ToolDecl[]): SystemMessage;
|
|
12
14
|
/** 系统提示(已装配的命名节)+ 工具声明的估算。 */
|
|
13
15
|
export declare function estimatePrefixTokens(sections: Readonly<Record<string, string>>, tools: readonly ToolDecl[]): number;
|
|
14
16
|
/** 上下文估算的来源(`SessionStats.context.source`)。 */
|
|
@@ -8,12 +8,16 @@
|
|
|
8
8
|
* 只用于统计:不写分支、不改请求体,前缀缓存不受影响;压缩阈值也不看它(摘要压不掉这部分)。
|
|
9
9
|
*/
|
|
10
10
|
import { estimateMessageTokens } from "./estimate.js";
|
|
11
|
-
/**
|
|
12
|
-
export function
|
|
11
|
+
/** 将要作为首条 `system` 消息发送的内容(只用于统计,不落盘)。 */
|
|
12
|
+
export function prefixSystemMessage(sections, tools) {
|
|
13
13
|
const message = { role: "system", sections: { ...sections }, timestamp: 0 };
|
|
14
14
|
if (tools.length > 0)
|
|
15
15
|
message.toolsAdded = [...tools];
|
|
16
|
-
return
|
|
16
|
+
return message;
|
|
17
|
+
}
|
|
18
|
+
/** 系统提示(已装配的命名节)+ 工具声明的估算。 */
|
|
19
|
+
export function estimatePrefixTokens(sections, tools) {
|
|
20
|
+
return estimateMessageTokens(prefixSystemMessage(sections, tools));
|
|
17
21
|
}
|
|
18
22
|
/**
|
|
19
23
|
* 给投影估算标来源;投影里还没有系统消息时把前缀基线加进去(`prefix` 懒算,只在需要时调用)。
|
package/dist/i18n/catalog.d.ts
CHANGED
|
@@ -1152,6 +1152,7 @@ declare const en: {
|
|
|
1152
1152
|
keySource: string;
|
|
1153
1153
|
sourceUsage: (usage: string, estimate: string) => string;
|
|
1154
1154
|
sourceEstimate: string;
|
|
1155
|
+
sourcePrefix: string;
|
|
1155
1156
|
keyAutoCompact: string;
|
|
1156
1157
|
autoCompact: (at: string, distance: string) => string;
|
|
1157
1158
|
autoCompactReached: (at: string) => string;
|
|
@@ -1162,6 +1163,7 @@ declare const en: {
|
|
|
1162
1163
|
breakdown: (total: string) => string;
|
|
1163
1164
|
prefixCounted: string;
|
|
1164
1165
|
prefixPending: string;
|
|
1166
|
+
prefixEstimated: string;
|
|
1165
1167
|
categories: {
|
|
1166
1168
|
system: string;
|
|
1167
1169
|
tools: string;
|
|
@@ -13,6 +13,7 @@ export declare const en: {
|
|
|
13
13
|
keySource: string;
|
|
14
14
|
sourceUsage: (usage: string, estimate: string) => string;
|
|
15
15
|
sourceEstimate: string;
|
|
16
|
+
sourcePrefix: string;
|
|
16
17
|
keyAutoCompact: string;
|
|
17
18
|
autoCompact: (at: string, distance: string) => string;
|
|
18
19
|
autoCompactReached: (at: string) => string;
|
|
@@ -23,6 +24,7 @@ export declare const en: {
|
|
|
23
24
|
breakdown: (total: string) => string;
|
|
24
25
|
prefixCounted: string;
|
|
25
26
|
prefixPending: string;
|
|
27
|
+
prefixEstimated: string;
|
|
26
28
|
categories: {
|
|
27
29
|
system: string;
|
|
28
30
|
tools: string;
|
|
@@ -57,6 +59,7 @@ export declare const zh: {
|
|
|
57
59
|
keySource: string;
|
|
58
60
|
sourceUsage: (usage: string, estimate: string) => string;
|
|
59
61
|
sourceEstimate: string;
|
|
62
|
+
sourcePrefix: string;
|
|
60
63
|
keyAutoCompact: string;
|
|
61
64
|
autoCompact: (at: string, distance: string) => string;
|
|
62
65
|
autoCompactReached: (at: string) => string;
|
|
@@ -67,6 +70,7 @@ export declare const zh: {
|
|
|
67
70
|
breakdown: (total: string) => string;
|
|
68
71
|
prefixCounted: string;
|
|
69
72
|
prefixPending: string;
|
|
73
|
+
prefixEstimated: string;
|
|
70
74
|
categories: {
|
|
71
75
|
system: string;
|
|
72
76
|
tools: string;
|
|
@@ -14,6 +14,7 @@ export const en = {
|
|
|
14
14
|
keySource: "Source",
|
|
15
15
|
sourceUsage: (usage, estimate) => `reported usage ${usage} + estimated ${estimate}`,
|
|
16
16
|
sourceEstimate: "estimated in full (no usable usage yet)",
|
|
17
|
+
sourcePrefix: "system prompt and tools only, estimated before the first request",
|
|
17
18
|
keyAutoCompact: "Auto-compact",
|
|
18
19
|
autoCompact: (at, distance) => `at ${at}, ≈ ${distance} to go`,
|
|
19
20
|
autoCompactReached: (at) => `at ${at}, reached`,
|
|
@@ -24,6 +25,7 @@ export const en = {
|
|
|
24
25
|
breakdown: (total) => `By category (estimated, ${total})`,
|
|
25
26
|
prefixCounted: "System prompt and tool declarations are counted from the stored system messages; the full estimate can differ from reported usage.",
|
|
26
27
|
prefixPending: "System prompt and tool declarations are not counted until the first request.",
|
|
28
|
+
prefixEstimated: "System prompt and tool declarations are estimated from what the first request will send.",
|
|
27
29
|
categories: {
|
|
28
30
|
system: "System prompt",
|
|
29
31
|
tools: "Tool declarations",
|
|
@@ -58,6 +60,7 @@ export const zh = {
|
|
|
58
60
|
keySource: "来源",
|
|
59
61
|
sourceUsage: (usage, estimate) => `usage 实测 ${usage} + 估算 ${estimate}`,
|
|
60
62
|
sourceEstimate: "全量估算(还没有可用的 usage)",
|
|
63
|
+
sourcePrefix: "首次请求前,只有系统提示与工具声明的估算",
|
|
61
64
|
keyAutoCompact: "自动压缩",
|
|
62
65
|
autoCompact: (at, distance) => `${at} 触发,还差 ≈ ${distance}`,
|
|
63
66
|
autoCompactReached: (at) => `${at} 触发,已达到`,
|
|
@@ -68,6 +71,7 @@ export const zh = {
|
|
|
68
71
|
breakdown: (total) => `按类别(估算,${total})`,
|
|
69
72
|
prefixCounted: "系统提示与工具声明按会话里存的 system 消息计入;全量估算可能与 usage 实测不同。",
|
|
70
73
|
prefixPending: "系统提示与工具声明在首次请求后才计入。",
|
|
74
|
+
prefixEstimated: "系统提示与工具声明按首次请求将要发送的内容估算。",
|
|
71
75
|
categories: {
|
|
72
76
|
system: "系统提示",
|
|
73
77
|
tools: "工具声明",
|
|
@@ -97,6 +97,7 @@ export declare const en: {
|
|
|
97
97
|
keySource: string;
|
|
98
98
|
sourceUsage: (usage: string, estimate: string) => string;
|
|
99
99
|
sourceEstimate: string;
|
|
100
|
+
sourcePrefix: string;
|
|
100
101
|
keyAutoCompact: string;
|
|
101
102
|
autoCompact: (at: string, distance: string) => string;
|
|
102
103
|
autoCompactReached: (at: string) => string;
|
|
@@ -107,6 +108,7 @@ export declare const en: {
|
|
|
107
108
|
breakdown: (total: string) => string;
|
|
108
109
|
prefixCounted: string;
|
|
109
110
|
prefixPending: string;
|
|
111
|
+
prefixEstimated: string;
|
|
110
112
|
categories: {
|
|
111
113
|
system: string;
|
|
112
114
|
tools: string;
|
|
@@ -339,6 +341,7 @@ export declare const zh: {
|
|
|
339
341
|
keySource: string;
|
|
340
342
|
sourceUsage: (usage: string, estimate: string) => string;
|
|
341
343
|
sourceEstimate: string;
|
|
344
|
+
sourcePrefix: string;
|
|
342
345
|
keyAutoCompact: string;
|
|
343
346
|
autoCompact: (at: string, distance: string) => string;
|
|
344
347
|
autoCompactReached: (at: string) => string;
|
|
@@ -349,6 +352,7 @@ export declare const zh: {
|
|
|
349
352
|
breakdown: (total: string) => string;
|
|
350
353
|
prefixCounted: string;
|
|
351
354
|
prefixPending: string;
|
|
355
|
+
prefixEstimated: string;
|
|
352
356
|
categories: {
|
|
353
357
|
system: string;
|
|
354
358
|
tools: string;
|
|
@@ -18,6 +18,8 @@ export declare const CONTEXT_TOP_RESULTS = 5;
|
|
|
18
18
|
export interface ContextSnapshot {
|
|
19
19
|
breakdown: ContextBreakdown;
|
|
20
20
|
estimate: ContextEstimate;
|
|
21
|
+
/** 首次请求前:系统提示与工具声明按将要发送的那一份估算(与状态栏的启动基线同一口径)。 */
|
|
22
|
+
prefixPending: boolean;
|
|
21
23
|
window: number | undefined;
|
|
22
24
|
/** 0–100(一位小数,封顶 100);无窗口时 undefined。 */
|
|
23
25
|
percent: number | undefined;
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* 统计只读:取活动分支的投影(与 `getStats().contextTokens` 同一口径),不改请求体、不写条目。
|
|
11
11
|
*/
|
|
12
12
|
import { AgentSessionImpl } from "../agent/session.js";
|
|
13
|
-
import { DEFAULT_COMPACTION_SETTINGS } from "../agent/session-compaction.js";
|
|
13
|
+
import { DEFAULT_COMPACTION_SETTINGS, pendingPrefixMessage } from "../agent/session-compaction.js";
|
|
14
14
|
import { breakdownMessages, largestToolResults, } from "../compaction/breakdown.js";
|
|
15
15
|
import { estimateContextTokens, estimateProjectedTokens, } from "../compaction/estimate.js";
|
|
16
16
|
import { prunePolicy } from "../compaction/prune-tier.js";
|
|
@@ -28,16 +28,25 @@ export function contextSnapshot(session) {
|
|
|
28
28
|
let messages = session.messages;
|
|
29
29
|
let estimate;
|
|
30
30
|
let compaction = {};
|
|
31
|
+
let prefixPending = false;
|
|
31
32
|
if (session instanceof AgentSessionImpl) {
|
|
32
33
|
const branch = session.manager.branch();
|
|
33
34
|
const items = buildProjection(branch).items;
|
|
34
35
|
messages = items.map((item) => item.message);
|
|
35
36
|
estimate = estimateProjectedTokens(items, branch);
|
|
36
37
|
compaction = (session.options.compaction ?? {});
|
|
38
|
+
if (!messages.some((message) => "role" in message && message.role === "system")) {
|
|
39
|
+
prefixPending = true;
|
|
40
|
+
messages = [pendingPrefixMessage(session), ...messages];
|
|
41
|
+
}
|
|
37
42
|
}
|
|
38
43
|
else
|
|
39
44
|
estimate = estimateContextTokens(messages);
|
|
40
|
-
const
|
|
45
|
+
const stats = session.getStats();
|
|
46
|
+
// 「已用」与状态栏、getStats 同一口径(含启动基线)
|
|
47
|
+
if (stats.contextTokens !== undefined)
|
|
48
|
+
estimate = { ...estimate, tokens: stats.contextTokens };
|
|
49
|
+
const window = stats.contextWindow;
|
|
41
50
|
const reserve = compaction.reserveTokens ?? DEFAULT_COMPACTION_SETTINGS.reserveTokens;
|
|
42
51
|
const budget = window === undefined ? undefined : Math.max(0, window - reserve);
|
|
43
52
|
const percent = window === undefined || window <= 0
|
|
@@ -46,6 +55,7 @@ export function contextSnapshot(session) {
|
|
|
46
55
|
return {
|
|
47
56
|
breakdown: breakdownMessages(messages),
|
|
48
57
|
estimate,
|
|
58
|
+
prefixPending,
|
|
49
59
|
window,
|
|
50
60
|
percent,
|
|
51
61
|
autoCompaction: session.state.autoCompaction,
|
|
@@ -69,9 +79,11 @@ function headerRows(snap) {
|
|
|
69
79
|
});
|
|
70
80
|
rows.push({
|
|
71
81
|
key: m.keySource,
|
|
72
|
-
value:
|
|
73
|
-
? m.
|
|
74
|
-
:
|
|
82
|
+
value: snap.prefixPending
|
|
83
|
+
? m.sourcePrefix
|
|
84
|
+
: estimate.lastUsageIndex === null
|
|
85
|
+
? m.sourceEstimate
|
|
86
|
+
: m.sourceUsage(formatTokenCount(estimate.usageTokens), formatTokenCount(estimate.trailingTokens)),
|
|
75
87
|
});
|
|
76
88
|
let auto;
|
|
77
89
|
if (!snap.autoCompaction)
|
|
@@ -132,7 +144,7 @@ export function describeContext(session) {
|
|
|
132
144
|
const { breakdown } = snap;
|
|
133
145
|
const lines = [m.title, ...renderRows(headerRows(snap), " "), ""];
|
|
134
146
|
lines.push(m.breakdown(formatTokenCount(breakdown.total)));
|
|
135
|
-
lines.push(` ${breakdown.hasSystem ? m.prefixCounted : m.prefixPending}`);
|
|
147
|
+
lines.push(` ${snap.prefixPending ? m.prefixEstimated : breakdown.hasSystem ? m.prefixCounted : m.prefixPending}`);
|
|
136
148
|
if (breakdown.total === 0)
|
|
137
149
|
lines.push(` ${m.empty}`);
|
|
138
150
|
else
|
|
@@ -157,7 +169,11 @@ export function contextPanel(session, theme) {
|
|
|
157
169
|
"",
|
|
158
170
|
theme.bold(m.breakdown(formatTokenCount(breakdown.total))),
|
|
159
171
|
new Indent(new Stack([
|
|
160
|
-
new Text(theme.fg("dim",
|
|
172
|
+
new Text(theme.fg("dim", snap.prefixPending
|
|
173
|
+
? m.prefixEstimated
|
|
174
|
+
: breakdown.hasSystem
|
|
175
|
+
? m.prefixCounted
|
|
176
|
+
: m.prefixPending)),
|
|
161
177
|
breakdown.total === 0
|
|
162
178
|
? theme.fg("dim", m.empty)
|
|
163
179
|
: table(categoryRows(breakdown, theme), true),
|
package/docs/en/tui.md
CHANGED
|
@@ -249,7 +249,7 @@ Design in [rewind-plan.md](../rewind-plan.md) (Chinese). Every user message that
|
|
|
249
249
|
|
|
250
250
|
- **Top**: used / window and percentage (the same measure as `/session`: the latest usable usage plus an estimate of the messages after it), tokens left, the source ("reported usage X + estimated Y"; "estimated in full" when there is no usable usage yet, or a compaction / context edit happened after it), the auto-compact trigger (window − `compaction.reserveTokens`; "off" when disabled, "window unknown" without a window) and where tier-one pruning starts.
|
|
251
251
|
- **By category**: system prompt (per section), tool declarations (per tool), user messages, assistant text, reasoning, tool-call arguments (per tool), tool results (summed per tool name), images (1,600 each), summaries (compaction / branch) and custom messages, each with an estimated token count, share and bar. The categories add up to the character-based estimate of the whole projection (one token per CJK character, other characters / 4), so the total in the heading can differ from "Used" at the top, which prefers the usage reported by the model.
|
|
252
|
-
- **System prompt and tool declarations** come from the system messages stored in the session (the first full one and later patches, matching the estimate); before the first request the branch has no system message yet, both categories are
|
|
252
|
+
- **System prompt and tool declarations** come from the system messages stored in the session (the first full one and later patches, matching the estimate); before the first request the branch has no system message yet, so both categories are estimated from what the first request will send (the same baseline as the status bar); "Used" matches the status bar and the source reads "system prompt and tools only, estimated before the first request".
|
|
253
253
|
- **Bottom**: only the ordinal (which tool result in the projection), tool name and size of the 5 largest tool results — **never their content**.
|
|
254
254
|
- Read-only: no session entries are written and the request body is untouched, so the prompt-cache prefix is unaffected.
|
|
255
255
|
|
package/docs/tui.md
CHANGED
|
@@ -243,7 +243,7 @@ line 模式(`--no-tui`)运行中输入 `/interrupt <文本>` 效果相同(
|
|
|
243
243
|
|
|
244
244
|
- **顶部**:已用 / 窗口与百分比(与 `/session` 同一口径:最后一条可用 usage + 其后消息的估算)、剩余、来源(「usage 实测 X + 估算 Y」;还没有可用 usage,或 usage 之后发生过压缩 / 上下文编辑时为「全量估算」)、自动压缩触发点(窗口 − `compaction.reserveTokens`,关闭时显示「关闭」,没有窗口时「窗口未知」)与档一裁剪的起点。
|
|
245
245
|
- **按类别**:系统提示(按节)、工具声明(按工具)、用户消息、助手文本、推理、工具调用参数(按工具)、工具结果(按工具名汇总)、附件图片(每张按 1 600 计)、摘要(压缩 / 分支)、自定义消息;每类给 token 估算、占比与条形。各类之和等于整个投影的字符估算(CJK 每字 1,其余 / 4),所以标题里的合计可能与顶部的「已用」不同——后者优先采信模型回报的 usage。
|
|
246
|
-
- **系统提示与工具声明**来自会话里存的 system 消息(首条全量、之后的补丁也计入,与估算口径一致);首次请求前分支上还没有 system
|
|
246
|
+
- **系统提示与工具声明**来自会话里存的 system 消息(首条全量、之后的补丁也计入,与估算口径一致);首次请求前分支上还没有 system 消息,这两类按首次请求将要发送的内容估算(与状态栏的启动基线同一口径),「已用」与状态栏一致,来源标为「首次请求前,只有系统提示与工具声明的估算」。
|
|
247
247
|
- **底部**只列最大的 5 个工具结果的序号(投影里第几个工具结果)、工具名与大小,**不显示正文**。
|
|
248
248
|
- 统计只读:不写会话条目、不改请求体,提示词缓存前缀不受影响。
|
|
249
249
|
|