@deepwhale/core 1.0.12 → 1.0.13

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.
@@ -0,0 +1,18 @@
1
+ /**
2
+ * i18n 模块 — 必须在 Sprint 0 第 1 行定对路径。
3
+ *
4
+ * 命名空间: `core.i18n`(不是 `gateway.i18n`,避免 Hermes 教训)。
5
+ * 所有包都用 `import { t } from '@deepwhale/core/i18n'` 引入。
6
+ */
7
+ import type { Locale, TranslationKey } from './types.js';
8
+ export declare function setLocale(locale: Locale): void;
9
+ export declare function getLocale(): Locale;
10
+ /**
11
+ * 翻译函数。Key 不存在时回退英文,英文也不存在时返回 key 本身。
12
+ *
13
+ * @example
14
+ * t('cli.greeting') // "Hello! I'm deepwhale"
15
+ */
16
+ export declare function t(key: TranslationKey, ...args: unknown[]): string;
17
+ export type { Locale, TranslationKey } from './types.js';
18
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/i18n/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAezD,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAE9C;AAED,wBAAgB,SAAS,IAAI,MAAM,CAElC;AAED;;;;;GAKG;AACH,wBAAgB,CAAC,CAAC,GAAG,EAAE,cAAc,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,MAAM,CAIjE;AAUD,YAAY,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * i18n 模块 — 必须在 Sprint 0 第 1 行定对路径。
3
+ *
4
+ * 命名空间: `core.i18n`(不是 `gateway.i18n`,避免 Hermes 教训)。
5
+ * 所有包都用 `import { t } from '@deepwhale/core/i18n'` 引入。
6
+ */
7
+ import { en } from './locales/en.js';
8
+ import { zh } from './locales/zh.js';
9
+ const locales = {
10
+ en,
11
+ 'zh-CN': zh,
12
+ };
13
+ let currentLocale = detectLocale();
14
+ function detectLocale() {
15
+ const env = process.env['DEEPWHALE_LANG'] ?? process.env['LANG'] ?? '';
16
+ if (env.toLowerCase().startsWith('zh'))
17
+ return 'zh-CN';
18
+ return 'en';
19
+ }
20
+ export function setLocale(locale) {
21
+ currentLocale = locale;
22
+ }
23
+ export function getLocale() {
24
+ return currentLocale;
25
+ }
26
+ /**
27
+ * 翻译函数。Key 不存在时回退英文,英文也不存在时返回 key 本身。
28
+ *
29
+ * @example
30
+ * t('cli.greeting') // "Hello! I'm deepwhale"
31
+ */
32
+ export function t(key, ...args) {
33
+ const dict = locales[currentLocale] ?? locales['en'];
34
+ const template = dict[key] ?? locales['en']?.[key] ?? key;
35
+ return format(template, args);
36
+ }
37
+ function format(template, args) {
38
+ if (args.length === 0)
39
+ return template;
40
+ return template.replace(/\{(\d+)\}/g, (_match, idx) => {
41
+ const i = Number.parseInt(idx, 10);
42
+ return String(args[i] ?? '');
43
+ });
44
+ }
45
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/i18n/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,EAAE,EAAE,MAAM,iBAAiB,CAAC;AACrC,OAAO,EAAE,EAAE,EAAE,MAAM,iBAAiB,CAAC;AAGrC,MAAM,OAAO,GAAmD;IAC9D,EAAE;IACF,OAAO,EAAE,EAAE;CACZ,CAAC;AAEF,IAAI,aAAa,GAAW,YAAY,EAAE,CAAC;AAE3C,SAAS,YAAY;IACnB,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;IACvE,IAAI,GAAG,CAAC,WAAW,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,OAAO,CAAC;IACvD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,MAAc;IACtC,aAAa,GAAG,MAAM,CAAC;AACzB,CAAC;AAED,MAAM,UAAU,SAAS;IACvB,OAAO,aAAa,CAAC;AACvB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,CAAC,CAAC,GAAmB,EAAE,GAAG,IAAe;IACvD,MAAM,IAAI,GAAG,OAAO,CAAC,aAAa,CAAC,IAAI,OAAO,CAAC,IAAI,CAAE,CAAC;IACtD,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC;IAC1D,OAAO,MAAM,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;AAChC,CAAC;AAED,SAAS,MAAM,CAAC,QAAgB,EAAE,IAAe;IAC/C,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,QAAQ,CAAC;IACvC,OAAO,QAAQ,CAAC,OAAO,CAAC,YAAY,EAAE,CAAC,MAAM,EAAE,GAAW,EAAE,EAAE;QAC5D,MAAM,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;QACnC,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;IAC/B,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { TranslationKey } from '../types.js';
2
+ export declare const en: Record<TranslationKey, string>;
3
+ //# sourceMappingURL=en.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"en.d.ts","sourceRoot":"","sources":["../../../src/i18n/locales/en.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD,eAAO,MAAM,EAAE,EAAE,MAAM,CAAC,cAAc,EAAE,MAAM,CAsB7C,CAAC"}
@@ -0,0 +1,24 @@
1
+ export const en = {
2
+ 'cli.greeting': "Hello! I'm deepwhale {0} 🐋, current model {1}",
3
+ 'cli.prompt': 'deepwhale> ',
4
+ 'cli.goodbye': 'Goodbye!',
5
+ 'cli.no_api_key_hint': "Type 'exit' to quit.",
6
+ 'cli.error.network': 'Network error: {0}',
7
+ 'cli.error.auth': 'Authentication failed ({0}). Check your DEEPSEEK_API_KEY.',
8
+ 'cli.error.rate_limit': 'Rate limited. Please try again later.',
9
+ 'cli.error.unknown': 'Unexpected error: {0}',
10
+ 'cli.error.stream': 'Stream interrupted: {0}',
11
+ 'cli.empty_input': '',
12
+ 'cli.builtin_help': 'Built-in commands: /help, /exit',
13
+ 'cli.builtin_exit': 'exit',
14
+ 'cli.builtin_unknown': 'Unknown command: {0}. Type /help for the list.',
15
+ 'cli.session_resumed': 'Resumed session with {0} messages from {1}',
16
+ 'cli.session_load_warning': 'Could not load session: {0}',
17
+ 'cli.session_write_warning': 'Could not write session event: {0}',
18
+ 'cli.tool_loop_limit': 'Tool loop hit max steps ({0}). Try a shorter task.',
19
+ 'cli.repl_force_exit_timeout': 'warning: in-flight turn did not finish within {0}ms, forcing REPL exit (audit may be incomplete)',
20
+ 'cli.turn_in_flight_deny': 'turn running, wait for finish (built-in command deferred)',
21
+ 'cli.turn_aborted_shutdown': 'turn aborted during shutdown (no audit gap)',
22
+ 'error.api_key_missing': 'DEEPSEEK_API_KEY is not set. Please set it in ~/.deepwhale/config.toml',
23
+ };
24
+ //# sourceMappingURL=en.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"en.js","sourceRoot":"","sources":["../../../src/i18n/locales/en.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,EAAE,GAAmC;IAChD,cAAc,EAAE,gDAAgD;IAChE,YAAY,EAAE,aAAa;IAC3B,aAAa,EAAE,UAAU;IACzB,qBAAqB,EAAE,sBAAsB;IAC7C,mBAAmB,EAAE,oBAAoB;IACzC,gBAAgB,EAAE,2DAA2D;IAC7E,sBAAsB,EAAE,uCAAuC;IAC/D,mBAAmB,EAAE,uBAAuB;IAC5C,kBAAkB,EAAE,yBAAyB;IAC7C,iBAAiB,EAAE,EAAE;IACrB,kBAAkB,EAAE,iCAAiC;IACrD,kBAAkB,EAAE,MAAM;IAC1B,qBAAqB,EAAE,gDAAgD;IACvE,qBAAqB,EAAE,4CAA4C;IACnE,0BAA0B,EAAE,6BAA6B;IACzD,2BAA2B,EAAE,oCAAoC;IACjE,qBAAqB,EAAE,oDAAoD;IAC3E,6BAA6B,EAAE,kGAAkG;IACjI,yBAAyB,EAAE,2DAA2D;IACtF,2BAA2B,EAAE,6CAA6C;IAC1E,uBAAuB,EAAE,wEAAwE;CAClG,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { TranslationKey } from '../types.js';
2
+ export declare const zh: Record<TranslationKey, string>;
3
+ //# sourceMappingURL=zh.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"zh.d.ts","sourceRoot":"","sources":["../../../src/i18n/locales/zh.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD,eAAO,MAAM,EAAE,EAAE,MAAM,CAAC,cAAc,EAAE,MAAM,CAsB7C,CAAC"}
@@ -0,0 +1,24 @@
1
+ export const zh = {
2
+ 'cli.greeting': '你好!我是 deepwhale {0} 🐋,当前模型 {1}',
3
+ 'cli.prompt': 'deepwhale> ',
4
+ 'cli.goodbye': '再见!',
5
+ 'cli.no_api_key_hint': "输入 'exit' 退出。",
6
+ 'cli.error.network': '网络错误:{0}',
7
+ 'cli.error.auth': '认证失败({0})。请检查 DEEPSEEK_API_KEY。',
8
+ 'cli.error.rate_limit': '请求过于频繁,请稍后再试。',
9
+ 'cli.error.unknown': '未知错误:{0}',
10
+ 'cli.error.stream': '流式响应中断:{0}',
11
+ 'cli.empty_input': '',
12
+ 'cli.builtin_help': '内建命令:/help、/exit',
13
+ 'cli.builtin_exit': 'exit',
14
+ 'cli.builtin_unknown': '未知命令:{0}。输入 /help 查看列表。',
15
+ 'cli.session_resumed': '已恢复会话:从 {1} 加载 {0} 条消息',
16
+ 'cli.session_load_warning': '加载会话失败:{0}',
17
+ 'cli.session_write_warning': '写入会话失败:{0}',
18
+ 'cli.tool_loop_limit': '工具循环达到最大步数({0})。请尝试更短的任务。',
19
+ 'cli.repl_force_exit_timeout': 'warning: 当前 turn 未在 {0}ms 内收束, 强制退出 REPL (审计可能不完整)',
20
+ 'cli.turn_in_flight_deny': 'turn 正在运行, 请等待完成 (内建命令已拒绝)',
21
+ 'cli.turn_aborted_shutdown': 'turn 在关闭过程中被中断 (审计完整)',
22
+ 'error.api_key_missing': '未设置 DEEPSEEK_API_KEY,请在 ~/.deepwhale/config.toml 配置',
23
+ };
24
+ //# sourceMappingURL=zh.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"zh.js","sourceRoot":"","sources":["../../../src/i18n/locales/zh.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,EAAE,GAAmC;IAChD,cAAc,EAAE,iCAAiC;IACjD,YAAY,EAAE,aAAa;IAC3B,aAAa,EAAE,KAAK;IACpB,qBAAqB,EAAE,eAAe;IACtC,mBAAmB,EAAE,UAAU;IAC/B,gBAAgB,EAAE,iCAAiC;IACnD,sBAAsB,EAAE,eAAe;IACvC,mBAAmB,EAAE,UAAU;IAC/B,kBAAkB,EAAE,YAAY;IAChC,iBAAiB,EAAE,EAAE;IACrB,kBAAkB,EAAE,kBAAkB;IACtC,kBAAkB,EAAE,MAAM;IAC1B,qBAAqB,EAAE,yBAAyB;IAChD,qBAAqB,EAAE,wBAAwB;IAC/C,0BAA0B,EAAE,YAAY;IACxC,2BAA2B,EAAE,YAAY;IACzC,qBAAqB,EAAE,2BAA2B;IAClD,6BAA6B,EAAE,oDAAoD;IACnF,yBAAyB,EAAE,4BAA4B;IACvD,2BAA2B,EAAE,uBAAuB;IACpD,uBAAuB,EAAE,qDAAqD;CAC/E,CAAC"}
@@ -0,0 +1,7 @@
1
+ export type Locale = 'en' | 'zh-CN';
2
+ /**
3
+ * 翻译 key 联合类型。Sprint 0 占位 4 个 key,Sprint 1a 加 session/tool_loop 相关。
4
+ * 新增 key 必须先在这里加,否则 t() 会回退到 key 字符串。
5
+ */
6
+ export type TranslationKey = 'cli.greeting' | 'cli.prompt' | 'cli.goodbye' | 'cli.no_api_key_hint' | 'cli.error.network' | 'cli.error.auth' | 'cli.error.rate_limit' | 'cli.error.unknown' | 'cli.error.stream' | 'cli.empty_input' | 'cli.builtin_help' | 'cli.builtin_exit' | 'cli.builtin_unknown' | 'cli.session_resumed' | 'cli.session_load_warning' | 'cli.session_write_warning' | 'cli.tool_loop_limit' | 'cli.repl_force_exit_timeout' | 'cli.turn_in_flight_deny' | 'cli.turn_aborted_shutdown' | 'error.api_key_missing';
7
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/i18n/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC;AAEpC;;;GAGG;AACH,MAAM,MAAM,cAAc,GACtB,cAAc,GACd,YAAY,GACZ,aAAa,GACb,qBAAqB,GACrB,mBAAmB,GACnB,gBAAgB,GAChB,sBAAsB,GACtB,mBAAmB,GACnB,kBAAkB,GAClB,iBAAiB,GACjB,kBAAkB,GAClB,kBAAkB,GAClB,qBAAqB,GACrB,qBAAqB,GACrB,0BAA0B,GAC1B,2BAA2B,GAC3B,qBAAqB,GACrB,6BAA6B,GAC7B,yBAAyB,GACzB,2BAA2B,GAC3B,uBAAuB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/i18n/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * @deepwhale/core — 跨包共享的原子原语 + i18n + 类型 + Session JSONL
3
+ *
4
+ * 这是 deepwhale 4 包 monorepo 的根。所有包都依赖 core。
5
+ * i18n 路径在 Sprint 0 第 1 行定对:core.i18n(避免 Hermes gateway.i18n 错误)。
6
+ */
7
+ export * from './i18n/index.js';
8
+ export * from './types/index.js';
9
+ export * from './session/jsonl.js';
10
+ export * from './session/compaction.js';
11
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,cAAc,iBAAiB,CAAC;AAChC,cAAc,kBAAkB,CAAC;AACjC,cAAc,oBAAoB,CAAC;AACnC,cAAc,yBAAyB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,11 @@
1
+ /**
2
+ * @deepwhale/core — 跨包共享的原子原语 + i18n + 类型 + Session JSONL
3
+ *
4
+ * 这是 deepwhale 4 包 monorepo 的根。所有包都依赖 core。
5
+ * i18n 路径在 Sprint 0 第 1 行定对:core.i18n(避免 Hermes gateway.i18n 错误)。
6
+ */
7
+ export * from './i18n/index.js';
8
+ export * from './types/index.js';
9
+ export * from './session/jsonl.js';
10
+ export * from './session/compaction.js';
11
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,cAAc,iBAAiB,CAAC;AAChC,cAAc,kBAAkB,CAAC;AACjC,cAAc,oBAAoB,CAAC;AACnC,cAAc,yBAAyB,CAAC"}
@@ -0,0 +1,235 @@
1
+ /**
2
+ * Session Compaction — Sprint 1c-revive-2-D-5-1
3
+ *
4
+ * 当 LLM context token 数逼近 window 上限时, 把前面的对话
5
+ * 总结成 1 条 summary, 替换 messages 中间段, 避免 OOM/超限.
6
+ *
7
+ * 拍板来源 (research/03_reasonix.md compact.go + research/04_pi.md session_before_compact):
8
+ * - 触发条件: promptTokens >= window * compactRatio (默认 0.8, 拍板 source: Reasonix)
9
+ * - Tail 边界: 保留最近 N 条消息 (D-5-1 = message count, D-5-3 升级到 token budget)
10
+ * - Death-loop 防护: 连续 2 次失败 → latch (D-5-2 拍板)
11
+ * - Compaction = 唯一 cache-reset point (改 system prompt 拍板要同步 review)
12
+ *
13
+ * D-5-1 范围 (本 commit):
14
+ * - 基础 trigger (shouldCompact / estimateTokens)
15
+ * - replace (compact 函数 + 'compaction' SessionEvent)
16
+ * - summary 注入: 走 LLM 拍板, 这里只接 summaryFn callback
17
+ * - 测: shouldCompact 边界 / compact 替换 / event kind
18
+ *
19
+ * D-5-2 拍板: stuck latch (连续 2 次失败 → 暂停 + 拍板)
20
+ * D-5-3 拍板: tail token budget 替代 message count
21
+ *
22
+ * @module @deepwhale/core/session/compaction
23
+ */
24
+ import type { SessionEvent } from './jsonl.js';
25
+ /**
26
+ * ChatMessage 本地 type 拍板 (跟 tool-loop.ts / session-adapter.ts 拍板一致).
27
+ *
28
+ * @core 不能 import @llm (tsconfig 没 reference, 会循环).
29
+ * 拍板: structural typing — 拍成最小可用形态, 跟 @llm ChatMessage 形状一致.
30
+ * 若 @llm ChatMessage 加字段, 这里需同步 (Sprint 1+ 抽 brand-typed union 时再 review).
31
+ *
32
+ * 字段:
33
+ * - role: system | user | assistant | tool
34
+ * - content: 文本
35
+ * - tool_calls?: assistant 携带
36
+ * - tool_call_id?: tool 消息 echo
37
+ * - name?: tool 消息携带
38
+ */
39
+ export type ChatMessage = {
40
+ role: 'system' | 'user' | 'assistant' | 'tool';
41
+ content: string;
42
+ tool_calls?: ReadonlyArray<{
43
+ readonly id: string;
44
+ readonly name: string;
45
+ readonly args: Record<string, unknown>;
46
+ }>;
47
+ tool_call_id?: string;
48
+ name?: string;
49
+ };
50
+ /** Compaction 拍板配置 */
51
+ export interface CompactionConfig {
52
+ /** 模型的 context window (token 数). 0 = 不触发 (Sprint 1c.5 拍板: 0 关闭) */
53
+ readonly contextWindow: number;
54
+ /** 触发阈值 = contextWindow * compactRatio. 默认 0.8 (Reasonix 拍板) */
55
+ readonly compactRatio?: number;
56
+ /**
57
+ * Tail 保留策略 (Sprint 1c-revive-2-D-5-3):
58
+ * - 'message_count' (D-5-1 拍板): 保留最后 N 条消息, 用 tailKeepMessages (默认 4)
59
+ * - 'token_budget' (D-5-3 拍板, **默认**): 保留最后 N token 消息, 用 tailKeepTokens (默认 500)
60
+ *
61
+ * Reasonix compact.go:271-289 拍板 source: tail 边界按 token budget 而非 message count,
62
+ * 拍板让 tail 大小不依赖消息数, 长 tool result 不被截断.
63
+ */
64
+ readonly tailMode?: 'message_count' | 'token_budget';
65
+ /** 保留最后 N 条消息不被总结. 默认 4 (仅 tailMode='message_count' 用) */
66
+ readonly tailKeepMessages?: number;
67
+ /** 保留最后 N token 消息不被总结. 默认 500 (仅 tailMode='token_budget' 用) */
68
+ readonly tailKeepTokens?: number;
69
+ /**
70
+ * 连续失败 latch 阈值 (Sprint 1c-revive-2-D-5-2):
71
+ * 连续 N 次 compact 失败 → 自动暂停 + 写 paused event. 默认 2 (Reasonix 拍板).
72
+ * 防止 death loop: LLM context 涨 → compact 失败 → 再涨 → 再 compact → 失败...
73
+ * 设为 0 / undefined = 不 latch (走纯失败重试, 不推荐).
74
+ */
75
+ readonly pauseAfterFailures?: number;
76
+ }
77
+ export declare const COMPACTION_DEFAULTS: {
78
+ readonly compactRatio: 0.8;
79
+ readonly tailMode: "token_budget";
80
+ readonly tailKeepMessages: 4;
81
+ readonly tailKeepTokens: 500;
82
+ readonly pauseAfterFailures: 2;
83
+ };
84
+ /**
85
+ * 估算 messages 的 token 数.
86
+ *
87
+ * Sprint 1c.5 拍板: 不引入 tiktoken 等依赖, 用 char/4 粗估.
88
+ * 准确度对 compaction 触发点足够 (0.8 阈值留 20% buffer).
89
+ *
90
+ * 估算口径:
91
+ * - role + content 全算
92
+ * - tool_calls 走 JSON.stringify 后 char/4
93
+ * - 工具调用 id/name 算 ~10 token 额外
94
+ */
95
+ export declare function estimateTokens(messages: ReadonlyArray<ChatMessage>): number;
96
+ /**
97
+ * 解析 tail 边界 (Sprint 1c-revive-2-D-5-3):
98
+ * 拍板 'message_count' 走 tailKeepMessages, 'token_budget' 走 tailKeepTokens.
99
+ *
100
+ * 返 { tailStart, head }:
101
+ * - tailStart: tail 段的起始 index (head = messages[0..tailStart))
102
+ * - tail: messages[tailStart..] (>= 1 条, 拍板不变量)
103
+ *
104
+ * 不变量: messages.length > 0 时 tail 至少 1 条
105
+ * messages.length == 0 时 tailStart = 0, tail = []
106
+ *
107
+ * Reasonix compact.go:271-289 拍板 source: tailStart 算到 token >= tailKeepTokens,
108
+ * 拍板让 tail 大小跟 message 数量解耦.
109
+ */
110
+ export declare function resolveTail(messages: ReadonlyArray<ChatMessage>, config: CompactionConfig): {
111
+ readonly tailStart: number;
112
+ readonly head: ReadonlyArray<ChatMessage>;
113
+ readonly tail: ReadonlyArray<ChatMessage>;
114
+ };
115
+ /** 合并 config + defaults, 算出有效触发阈值 + tail */
116
+ export declare function resolveCompactionConfig(config: CompactionConfig): {
117
+ readonly contextWindow: number;
118
+ readonly compactRatio: number;
119
+ readonly tailMode: 'message_count' | 'token_budget';
120
+ readonly tailKeepMessages: number;
121
+ readonly tailKeepTokens: number;
122
+ readonly pauseAfterFailures: number;
123
+ readonly threshold: number;
124
+ };
125
+ /**
126
+ * Compaction 状态机 (Sprint 1c-revive-2-D-5-2):
127
+ * 跟踪连续失败次数, 达到阈值 → latch → 暂停 + 写 paused event.
128
+ *
129
+ * 不变量:
130
+ * - consecutiveFailures 永不为负
131
+ * - paused === true ⇒ consecutiveFailures >= pauseThreshold (且未重置)
132
+ * - 1 次成功 → consecutiveFailures = 0 (无论之前几次失败)
133
+ * - 1 次失败 → consecutiveFailures++; 若 >= pauseThreshold → paused = true
134
+ *
135
+ * 重置 latch:
136
+ * - new CompactionState() 重新初始化
137
+ * - caller 决定何时调用 (e.g. 用户改 summaryFn / 改配置)
138
+ */
139
+ export declare class CompactionState {
140
+ private readonly pauseThreshold;
141
+ consecutiveFailures: number;
142
+ paused: boolean;
143
+ lastError: string | null;
144
+ constructor(pauseThreshold: number);
145
+ /** 1 次成功 → reset 失败计数 + unpause */
146
+ recordSuccess(): void;
147
+ /**
148
+ * 1 次失败 → 计数 +1, 达到阈值 → latch.
149
+ * 返 true 表示本次失败触发了 latch (caller 该写 paused event).
150
+ */
151
+ recordFailure(error: Error): boolean;
152
+ /** 该不该尝试 compact (paused → false) */
153
+ shouldAttempt(): boolean;
154
+ /** Caller 主动重置 latch (e.g. 用户改配置后) */
155
+ reset(): void;
156
+ }
157
+ /**
158
+ * Latched compact (Sprint 1c-revive-2-D-5-2):
159
+ * 把 shouldCompact + compact + latch 拍成 1 个函数.
160
+ *
161
+ * 拍板:
162
+ * - paused → 返 null (不尝试, 不调 summaryFn, 不写 event)
163
+ * - 不该 compact → 返 null
164
+ * - compact 成功 → recordSuccess, 返 { kind: 'ok', result, event }
165
+ * - compact 失败 → recordFailure
166
+ * - 触发 latch → 返 { kind: 'latched', error, pausedEvent }
167
+ * - 未触发 latch → 抛错给 caller
168
+ *
169
+ * 不变量: paused 时**不**调 summaryFn (避免 LLM 浪费 token).
170
+ */
171
+ export type LatchedCompactResult = {
172
+ readonly kind: 'ok';
173
+ readonly result: CompactionResult;
174
+ readonly event: SessionEvent;
175
+ } | {
176
+ readonly kind: 'latched';
177
+ readonly error: Error;
178
+ readonly pausedEvent: SessionEvent;
179
+ readonly consecutiveFailures: number;
180
+ };
181
+ export declare function runCompactionWithLatch(messages: ReadonlyArray<ChatMessage>, config: CompactionConfig, summaryFn: SummarizeFn, state: CompactionState, options?: {
182
+ now?: () => number;
183
+ }): Promise<LatchedCompactResult | null>;
184
+ /**
185
+ * 拍板: 当前 messages 是否该 compact.
186
+ *
187
+ * 规则:
188
+ * - contextWindow = 0 → 永远不 (拍板关闭)
189
+ * - messages 数 == 0 → false (没东西可 compact)
190
+ * - resolveTail 后 head 为空 → false (tail 已占满, 总结不掉什么)
191
+ * - estimated tokens >= threshold → true
192
+ */
193
+ export declare function shouldCompact(messages: ReadonlyArray<ChatMessage>, config: CompactionConfig): boolean;
194
+ /** Compaction 拍板结果 */
195
+ export interface CompactionResult {
196
+ /** 替换后的 messages (中间段被 1 条 summary 替代) */
197
+ readonly messages: ReadonlyArray<ChatMessage>;
198
+ /** 写入 JSONL 的 compaction event (供 SessionWriter.append 用) */
199
+ readonly event: SessionEvent;
200
+ /** 触发的统计 (供上层日志/UI) */
201
+ readonly stats: {
202
+ readonly beforeTokens: number;
203
+ readonly afterTokens: number;
204
+ readonly beforeMessages: number;
205
+ readonly afterMessages: number;
206
+ readonly replacedRange: readonly [number, number];
207
+ };
208
+ }
209
+ /**
210
+ * Summary 生成函数.
211
+ *
212
+ * 接 LLM 拍板 (caller 决定用哪个 client + system prompt).
213
+ * 拍板不变量: 收到被总结的 messages, 返回 summary text.
214
+ */
215
+ export type SummarizeFn = (messagesToSummarize: ReadonlyArray<ChatMessage>) => Promise<string>;
216
+ /**
217
+ * 执行 compaction.
218
+ *
219
+ * 流程:
220
+ * 1. 拍定 [head, tail) 中间段要被总结
221
+ * 2. 调 summaryFn 生成 summary text
222
+ * 3. 拼成新 messages: [{ role: 'system', content: summary }, ...tail]
223
+ * **删** head (旧实现 [...head, summary, ...tail] 把 head 留下来了 → 上下文反而
224
+ * 继续膨胀, 反复总结同一批旧 messages. 拍板 2026-06-04 review: summary 必须
225
+ * **替代** head, 不并存.)
226
+ * 4. 拍 'compaction' event: { summary, replaced_range: [0, head.length) }
227
+ * 不变量: replaced_range 拍的是"基于入参 messages 的 index", caller 调 compact()
228
+ * 时传什么 messages, replaced_range 就拍什么 index. 拍板 session reload 时
229
+ * replay 此 event 拍一致.
230
+ * 5. 返回 CompactionResult (不写盘, 由 caller append event)
231
+ */
232
+ export declare function compact(messages: ReadonlyArray<ChatMessage>, config: CompactionConfig, summaryFn: SummarizeFn, options?: {
233
+ now?: () => number;
234
+ }): Promise<CompactionResult>;
235
+ //# sourceMappingURL=compaction.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compaction.d.ts","sourceRoot":"","sources":["../../src/session/compaction.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,EAAE,QAAQ,GAAG,MAAM,GAAG,WAAW,GAAG,MAAM,CAAC;IAC/C,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,aAAa,CAAC;QACzB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;QACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KACxC,CAAC,CAAC;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,sBAAsB;AACtB,MAAM,WAAW,gBAAgB;IAC/B,mEAAmE;IACnE,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,gEAAgE;IAChE,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B;;;;;;;OAOG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,eAAe,GAAG,cAAc,CAAC;IACrD,0DAA0D;IAC1D,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IACnC,gEAAgE;IAChE,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC;;;;;OAKG;IACH,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAC;CACtC;AAED,eAAO,MAAM,mBAAmB;;;;;;CAMtB,CAAC;AAEX;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,aAAa,CAAC,WAAW,CAAC,GAAG,MAAM,CAe3E;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,WAAW,CACzB,QAAQ,EAAE,aAAa,CAAC,WAAW,CAAC,EACpC,MAAM,EAAE,gBAAgB,GACvB;IAAE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC,WAAW,CAAC,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC,WAAW,CAAC,CAAA;CAAE,CAiCtH;AAED,4CAA4C;AAC5C,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,gBAAgB,GAAG;IACjE,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,QAAQ,EAAE,eAAe,GAAG,cAAc,CAAC;IACpD,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B,CAeA;AAED;;;;;;;;;;;;;GAaG;AACH,qBAAa,eAAe;IAKd,OAAO,CAAC,QAAQ,CAAC,cAAc;IAJ3C,mBAAmB,SAAK;IACxB,MAAM,UAAS;IACf,SAAS,EAAE,MAAM,GAAG,IAAI,CAAQ;gBAEH,cAAc,EAAE,MAAM;IAMnD,mCAAmC;IACnC,aAAa,IAAI,IAAI;IAMrB;;;OAGG;IACH,aAAa,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO;IAUpC,qCAAqC;IACrC,aAAa,IAAI,OAAO;IAIxB,sCAAsC;IACtC,KAAK,IAAI,IAAI;CAKd;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,oBAAoB,GAC5B;IAAE,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,gBAAgB,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAA;CAAE,GACxF;IACE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IACtB,QAAQ,CAAC,WAAW,EAAE,YAAY,CAAC;IACnC,QAAQ,CAAC,mBAAmB,EAAE,MAAM,CAAC;CACtC,CAAC;AAEN,wBAAsB,sBAAsB,CAC1C,QAAQ,EAAE,aAAa,CAAC,WAAW,CAAC,EACpC,MAAM,EAAE,gBAAgB,EACxB,SAAS,EAAE,WAAW,EACtB,KAAK,EAAE,eAAe,EACtB,OAAO,GAAE;IAAE,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;CAAO,GACnC,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC,CAsCtC;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,aAAa,CAAC,WAAW,CAAC,EACpC,MAAM,EAAE,gBAAgB,GACvB,OAAO,CAQT;AAED,sBAAsB;AACtB,MAAM,WAAW,gBAAgB;IAC/B,0CAA0C;IAC1C,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC,WAAW,CAAC,CAAC;IAC9C,6DAA6D;IAC7D,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC;IAC7B,uBAAuB;IACvB,QAAQ,CAAC,KAAK,EAAE;QACd,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;QAC9B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;QAC7B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;QAChC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;QAC/B,QAAQ,CAAC,aAAa,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KACnD,CAAC;CACH;AAED;;;;;GAKG;AACH,MAAM,MAAM,WAAW,GAAG,CACxB,mBAAmB,EAAE,aAAa,CAAC,WAAW,CAAC,KAC5C,OAAO,CAAC,MAAM,CAAC,CAAC;AAErB;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,OAAO,CAC3B,QAAQ,EAAE,aAAa,CAAC,WAAW,CAAC,EACpC,MAAM,EAAE,gBAAgB,EACxB,SAAS,EAAE,WAAW,EACtB,OAAO,GAAE;IAAE,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;CAAO,GACnC,OAAO,CAAC,gBAAgB,CAAC,CAqD3B"}