dsh-layered-memory 0.8.8 → 0.8.9

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.
@@ -1,9 +1,12 @@
1
1
  import { createUserMessage } from '@deepseek-ai/dsh-llm';
2
2
  import { RecallDedupeStore } from '../store/recall-dedupe.js';
3
+ import { OccupancyStore } from '../store/occupancy.js';
3
4
  import { applyRecallBudget, raceRecallTimeout, RECALL_EMBED_CAP_MS } from '../util/recall-budget.js';
5
+ import { clearProfileShare, emptyOccupancyLedger, estimateInjectedMessageTokens, estimateStableSectionTokens, recordProfileShare, recordRecallInjection, resetForCompaction, } from '../util/context-occupancy.js';
4
6
  import { errDetail } from '../util/filelog.js';
5
7
  import { blocksToText } from '../util/text.js';
6
8
  const PROFILE_TTL = 60_000;
9
+ const storedEstimateCache = new Map();
7
10
  /** 召回查询只取会话末尾 N 条消息(长会话每步把全史拼进 FTS MATCH 会让检索成本线性上涨)。 */
8
11
  const RECALL_QUERY_TAIL_MESSAGES = 8;
9
12
  /** 召回查询总字符上限(保留末尾——最新语境权重最高)。 */
@@ -52,6 +55,8 @@ export function emptyRecallStats(now = Date.now()) {
52
55
  export function registerRecall(ctx, cfg, stores, logger, live, modes, dataDir) {
53
56
  /** 召回去重存储(同会话已注入的记忆不再重复注入;写穿持久化,重启不丢)。 */
54
57
  const dedupe = new RecallDedupeStore(dataDir, logger);
58
+ /** 记忆占用流水(账本迁移写穿;重启后历史会话账目由此复生——票07)。 */
59
+ const occupancyStore = new OccupancyStore(dataDir, logger);
55
60
  /** 每 agent 召回统计(工具指南门控读 lastHits;悬浮卡信息区读全量计数)。 */
56
61
  const recallStats = new Map();
57
62
  const statFor = (id) => {
@@ -62,6 +67,17 @@ export function registerRecall(ctx, cfg, stores, logger, live, modes, dataDir) {
62
67
  }
63
68
  return s;
64
69
  };
70
+ /** 每 agent 记忆占用账本(权威账本的唯一宿主实例;占用指示器与悬浮卡同源消费)。 */
71
+ const occupancyByAgent = new Map();
72
+ const ledgerFor = (id) => {
73
+ let led = occupancyByAgent.get(id);
74
+ if (!led) {
75
+ // 进程重启/agent 重建后回看:从流水复生(新迁移在持久值上继续累加——票07)
76
+ led = occupancyStore.load(id) ?? emptyOccupancyLedger();
77
+ occupancyByAgent.set(id, led);
78
+ }
79
+ return led;
80
+ };
65
81
  // 画像/场景导航按族缓存(分族隔离:注入时按会话档位选族)
66
82
  const profileCache = {
67
83
  chat: { persona: '', nav: '' },
@@ -92,13 +108,18 @@ export function registerRecall(ctx, cfg, stores, logger, live, modes, dataDir) {
92
108
  // agent 销毁时清掉召回统计槽(去重记录不随 agent 清——持久化语义:会话恢复后继续压制)
93
109
  ctx.on('agent/disposed', (payload) => {
94
110
  recallStats.delete(payload.agent.id);
111
+ occupancyByAgent.delete(payload.agent.id);
95
112
  });
96
113
  // 上下文压缩/清空 → 已注入内容从模型上下文丢失,重置该会话的去重压制
97
114
  // (resume/startup 不重置:历史仍在,已注入的记忆模型还持有)。
115
+ // 占用账本同步全量归零(宁低勿高;v1 轮级粒度近似,事件级 shadow price 对齐留待后续)。
98
116
  ctx.on('agent/session-start', (payload) => {
99
117
  if (payload.source === 'compact' || payload.source === 'clear') {
100
118
  dedupe.reset(payload.agent.id);
101
- logger.info(`[memory] 召回去重重置(agent=${payload.agent.id},source=${payload.source})`);
119
+ const led = ledgerFor(payload.agent.id);
120
+ resetForCompaction(led);
121
+ occupancyStore.save(payload.agent.id, led); // stock 归零 ⇒ 流水条目删除
122
+ logger.info(`[memory] 召回去重与占用账本重置(agent=${payload.agent.id},source=${payload.source})`);
102
123
  }
103
124
  });
104
125
  // ── 1. pre-step 消息侧注入:记忆先行于每一条新的用户输入(ADR-0001) ──
@@ -180,6 +201,10 @@ export function registerRecall(ctx, cfg, stores, logger, live, modes, dataDir) {
180
201
  // "上下文注入"(专用"跨会话召回"标题仅留给 session-reference 来源)
181
202
  source: { kind: 'plugin', plugin: 'memory', form: 'recall' },
182
203
  });
204
+ // 入账在成功构造注入消息之后、返回 enter 之前——任何前置抛错路径账目零扰动
205
+ const led = ledgerFor(payload.agent.id);
206
+ recordRecallInjection(led, text.length);
207
+ occupancyStore.save(payload.agent.id, led);
183
208
  // 注入消息排在用户新消息之前(原版 prepend 语义:先线索后问题)
184
209
  return { kind: 'enter', messages: [injection, ...decision.messages] };
185
210
  }
@@ -192,6 +217,101 @@ export function registerRecall(ctx, cfg, stores, logger, live, modes, dataDir) {
192
217
  // ── 2. agent 作用域上下文 provider(系统提示稳定区:画像 + 导航 + 门控指南) ──
193
218
  // 插件可能在默认 agent 创建之后才加载(组合顺序由依赖决定),
194
219
  // 因此除了监听 agent/created,还要给已存在的 agent 补注册。
220
+ /**
221
+ * 稳定区当前组词(纯读,不记账):text() 的取词部分单独成函数,
222
+ * 供旧会话回填估算(RecallHooks.estimateProfileTokens)复用同一口径。
223
+ */ const composeStableText = (agentId) => {
224
+ const s = live.get();
225
+ if (!s.enabled || !s.recall)
226
+ return '';
227
+ const mode = modes.get(agentId);
228
+ if (mode === 'off')
229
+ return '';
230
+ // auto 档:两族按类别归组(画像/导航各一个标签,域内 <domain> 分块);纯档:单族原格式
231
+ const body = mode === 'auto'
232
+ ? formatProfileAuto(profileCache.chat, profileCache.work)
233
+ : formatProfileSingle(profileCache[mode]);
234
+ const hasRecallHit = (recallStats.get(agentId)?.lastHits ?? 0) > 0;
235
+ // 指南三条件门控:工具已注册(cfg.tools)&&(稳定内容 ∥ 本轮召回命中)——
236
+ // 空库用户与关闭工具的用户不付这份固定 token(原版 auto-recall 同款语义)
237
+ if (!cfg.tools)
238
+ return body;
239
+ if (!body && !hasRecallHit)
240
+ return '';
241
+ return body ? `${body}\n\n${MEMORY_TOOLS_GUIDE}` : MEMORY_TOOLS_GUIDE;
242
+ };
243
+ async function estimateRecallFromStorage(sessionId) {
244
+ if (storedEstimateCache.has(sessionId))
245
+ return storedEstimateCache.get(sessionId) ?? null;
246
+ let tokens = null;
247
+ try {
248
+ // 可选服务(JSONL 后端注册名);缺失/其它实现 → 回填隐藏
249
+ const persistence = (await ctx.get?.('sessionPersistence'));
250
+ const stored = typeof persistence?.loadStored === 'function' ? await persistence.loadStored(sessionId) : undefined;
251
+ if (stored?.events) {
252
+ tokens = 0;
253
+ for (const ev of stored.events) {
254
+ if (typeof ev.type === 'string' && ev.type.startsWith('compaction'))
255
+ tokens = 0;
256
+ if (ev.type !== 'user/message')
257
+ continue;
258
+ const src = ev.data?.source;
259
+ if (!src || src.kind !== 'plugin' || src.plugin !== 'memory' || src.form !== 'recall')
260
+ continue;
261
+ let chars = 0;
262
+ for (const b of ev.data?.content ?? []) {
263
+ if (b?.type === 'text' && typeof b.text === 'string')
264
+ chars += b.text.length;
265
+ }
266
+ if (chars > 0)
267
+ tokens += estimateInjectedMessageTokens(chars);
268
+ }
269
+ }
270
+ }
271
+ catch {
272
+ tokens = null;
273
+ }
274
+ storedEstimateCache.set(sessionId, tokens);
275
+ return tokens;
276
+ }
277
+ /**
278
+ * 召回份额回填(票08 旧会话):live 会话的 surface(模型可见序号集)∩ 全事件日志
279
+ * 里本插件的 recall 注入,官方同式折算。窗口语义天然正确——被压缩折叠的注入不在
280
+ * surface.nodes 上,自动出局。会话不在 live store(未打开)返回 null。
281
+ */
282
+ const estimateRecallTokens = async (sessionId) => {
283
+ try {
284
+ // cordis 属性访问(ctx.sessions)对未 inject 的服务抛 "without inject"(实测);
285
+ // 可选服务一律走 ctx.get() 的宽容路径
286
+ const sessions = ctx.get?.('sessions');
287
+ const session = typeof sessions?.get === 'function' ? sessions.get(sessionId) : undefined;
288
+ if (session) {
289
+ const visible = new Set(session.surface.nodes);
290
+ let total = 0;
291
+ for (const ev of session.events) {
292
+ if (ev.type !== 'user/message' || !visible.has(ev.seq))
293
+ continue;
294
+ const msg = ev.data;
295
+ const src = msg?.source;
296
+ if (!src || src.kind !== 'plugin' || src.plugin !== 'memory' || src.form !== 'recall')
297
+ continue;
298
+ let chars = 0;
299
+ for (const b of msg.content ?? []) {
300
+ if (b?.type === 'text' && typeof b.text === 'string')
301
+ chars += b.text.length;
302
+ }
303
+ if (chars > 0)
304
+ total += estimateInjectedMessageTokens(chars);
305
+ }
306
+ return total;
307
+ }
308
+ // 仅查看的旧会话不在 live store:官方持久化服务读存储前缀兜底(见函数头)
309
+ return estimateRecallFromStorage(sessionId);
310
+ }
311
+ catch {
312
+ return null; // 服务缺失/形状异常:回填隐藏,不扰动主流程
313
+ }
314
+ };
195
315
  const registered = new WeakSet();
196
316
  // context() 的 disposer 必须挂到插件自身生命周期:agent.ctx 比插件实例活得久,
197
317
  // 不主动清理会导致热重载后旧注册泄漏、新实例撞名("already registered")
@@ -205,24 +325,15 @@ export function registerRecall(ctx, cfg, stores, logger, live, modes, dataDir) {
205
325
  name: 'memory:profile',
206
326
  order: 510,
207
327
  text: () => {
208
- const s = live.get();
209
- if (!s.enabled || !s.recall)
210
- return '';
211
- const mode = modes.get(agent.id);
212
- if (mode === 'off')
213
- return '';
214
- // auto 档:两族按类别归组(画像/导航各一个标签,域内 <domain> 分块);纯档:单族原格式
215
- const body = mode === 'auto'
216
- ? formatProfileAuto(profileCache.chat, profileCache.work)
217
- : formatProfileSingle(profileCache[mode]);
218
- const hasRecallHit = (recallStats.get(agent.id)?.lastHits ?? 0) > 0;
219
- // 指南三条件门控:工具已注册(cfg.tools)&&(稳定内容 ∥ 本轮召回命中)——
220
- // 空库用户与关闭工具的用户不付这份固定 token(原版 auto-recall 同款语义)
221
- if (!cfg.tools)
222
- return body;
223
- if (!body && !hasRecallHit)
224
- return '';
225
- return body ? `${body}\n\n${MEMORY_TOOLS_GUIDE}` : MEMORY_TOOLS_GUIDE;
328
+ const final = composeStableText(agent.id);
329
+ const ledger = ledgerFor(agent.id);
330
+ // 空串即物理离场(停用/OFF/门控全空):份额同边界清零;否则按实际长度入账
331
+ if (final === '')
332
+ clearProfileShare(ledger);
333
+ else
334
+ recordProfileShare(ledger, final.length);
335
+ occupancyStore.save(agent.id, ledger);
336
+ return final;
226
337
  },
227
338
  }));
228
339
  }
@@ -248,7 +359,19 @@ export function registerRecall(ctx, cfg, stores, logger, live, modes, dataDir) {
248
359
  }
249
360
  }
250
361
  });
251
- return { invalidateProfile, stats: (id) => recallStats.get(id) };
362
+ return {
363
+ invalidateProfile,
364
+ stats: (id) => recallStats.get(id),
365
+ /** 占用账本只读出口:内存优先,miss 时从流水复生(重启后历史会话);从未注入返回 null。 */
366
+ occupancy: (id) => {
367
+ const led = occupancyByAgent.get(id) ?? occupancyStore.load(id);
368
+ if (led)
369
+ occupancyByAgent.set(id, led);
370
+ return led ?? null;
371
+ },
372
+ estimateProfileTokens: (id) => estimateStableSectionTokens(composeStableText(id).length),
373
+ estimateRecallTokens,
374
+ };
252
375
  }
253
376
  /** auto 档 <user-persona> 内的域说明:让模型理解分块结构与两域的独立性。 */
254
377
  const DOMAIN_HINT = '以下内容按记忆域分块:chat=用户个人画像(User Narrative Profile),work=团队工作准则(Team Operating Doctrine)。' +
package/dist/index.d.ts CHANGED
@@ -109,6 +109,63 @@ export declare const Config: import("@deepseek-ai/schemastery").default<Schemast
109
109
  model: import("@deepseek-ai/schemastery").default<string, string>;
110
110
  reasoningEffort: import("@deepseek-ai/schemastery").default<"" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max", "" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max">;
111
111
  }>[]>;
112
+ layerRoutes: import("@deepseek-ai/schemastery").default<Schemastery.ObjectS<{
113
+ l1: import("@deepseek-ai/schemastery").default<({
114
+ provider?: string | null | undefined;
115
+ model?: string | null | undefined;
116
+ reasoningEffort?: "" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max" | null | undefined;
117
+ } & import("@deepseek-ai/cosmokit").Dict)[], Schemastery.ObjectT<{
118
+ provider: import("@deepseek-ai/schemastery").default<string, string>;
119
+ model: import("@deepseek-ai/schemastery").default<string, string>;
120
+ reasoningEffort: import("@deepseek-ai/schemastery").default<"" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max", "" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max">;
121
+ }>[]>;
122
+ l2: import("@deepseek-ai/schemastery").default<({
123
+ provider?: string | null | undefined;
124
+ model?: string | null | undefined;
125
+ reasoningEffort?: "" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max" | null | undefined;
126
+ } & import("@deepseek-ai/cosmokit").Dict)[], Schemastery.ObjectT<{
127
+ provider: import("@deepseek-ai/schemastery").default<string, string>;
128
+ model: import("@deepseek-ai/schemastery").default<string, string>;
129
+ reasoningEffort: import("@deepseek-ai/schemastery").default<"" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max", "" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max">;
130
+ }>[]>;
131
+ l3: import("@deepseek-ai/schemastery").default<({
132
+ provider?: string | null | undefined;
133
+ model?: string | null | undefined;
134
+ reasoningEffort?: "" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max" | null | undefined;
135
+ } & import("@deepseek-ai/cosmokit").Dict)[], Schemastery.ObjectT<{
136
+ provider: import("@deepseek-ai/schemastery").default<string, string>;
137
+ model: import("@deepseek-ai/schemastery").default<string, string>;
138
+ reasoningEffort: import("@deepseek-ai/schemastery").default<"" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max", "" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max">;
139
+ }>[]>;
140
+ }>, Schemastery.ObjectT<{
141
+ l1: import("@deepseek-ai/schemastery").default<({
142
+ provider?: string | null | undefined;
143
+ model?: string | null | undefined;
144
+ reasoningEffort?: "" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max" | null | undefined;
145
+ } & import("@deepseek-ai/cosmokit").Dict)[], Schemastery.ObjectT<{
146
+ provider: import("@deepseek-ai/schemastery").default<string, string>;
147
+ model: import("@deepseek-ai/schemastery").default<string, string>;
148
+ reasoningEffort: import("@deepseek-ai/schemastery").default<"" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max", "" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max">;
149
+ }>[]>;
150
+ l2: import("@deepseek-ai/schemastery").default<({
151
+ provider?: string | null | undefined;
152
+ model?: string | null | undefined;
153
+ reasoningEffort?: "" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max" | null | undefined;
154
+ } & import("@deepseek-ai/cosmokit").Dict)[], Schemastery.ObjectT<{
155
+ provider: import("@deepseek-ai/schemastery").default<string, string>;
156
+ model: import("@deepseek-ai/schemastery").default<string, string>;
157
+ reasoningEffort: import("@deepseek-ai/schemastery").default<"" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max", "" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max">;
158
+ }>[]>;
159
+ l3: import("@deepseek-ai/schemastery").default<({
160
+ provider?: string | null | undefined;
161
+ model?: string | null | undefined;
162
+ reasoningEffort?: "" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max" | null | undefined;
163
+ } & import("@deepseek-ai/cosmokit").Dict)[], Schemastery.ObjectT<{
164
+ provider: import("@deepseek-ai/schemastery").default<string, string>;
165
+ model: import("@deepseek-ai/schemastery").default<string, string>;
166
+ reasoningEffort: import("@deepseek-ai/schemastery").default<"" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max", "" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max">;
167
+ }>[]>;
168
+ }>>;
112
169
  maxTokens: import("@deepseek-ai/schemastery").default<number, number>;
113
170
  reasoningEffort: import("@deepseek-ai/schemastery").default<"" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max", "" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max">;
114
171
  temperature: import("@deepseek-ai/schemastery").default<number, number>;
@@ -126,6 +183,63 @@ export declare const Config: import("@deepseek-ai/schemastery").default<Schemast
126
183
  model: import("@deepseek-ai/schemastery").default<string, string>;
127
184
  reasoningEffort: import("@deepseek-ai/schemastery").default<"" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max", "" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max">;
128
185
  }>[]>;
186
+ layerRoutes: import("@deepseek-ai/schemastery").default<Schemastery.ObjectS<{
187
+ l1: import("@deepseek-ai/schemastery").default<({
188
+ provider?: string | null | undefined;
189
+ model?: string | null | undefined;
190
+ reasoningEffort?: "" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max" | null | undefined;
191
+ } & import("@deepseek-ai/cosmokit").Dict)[], Schemastery.ObjectT<{
192
+ provider: import("@deepseek-ai/schemastery").default<string, string>;
193
+ model: import("@deepseek-ai/schemastery").default<string, string>;
194
+ reasoningEffort: import("@deepseek-ai/schemastery").default<"" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max", "" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max">;
195
+ }>[]>;
196
+ l2: import("@deepseek-ai/schemastery").default<({
197
+ provider?: string | null | undefined;
198
+ model?: string | null | undefined;
199
+ reasoningEffort?: "" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max" | null | undefined;
200
+ } & import("@deepseek-ai/cosmokit").Dict)[], Schemastery.ObjectT<{
201
+ provider: import("@deepseek-ai/schemastery").default<string, string>;
202
+ model: import("@deepseek-ai/schemastery").default<string, string>;
203
+ reasoningEffort: import("@deepseek-ai/schemastery").default<"" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max", "" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max">;
204
+ }>[]>;
205
+ l3: import("@deepseek-ai/schemastery").default<({
206
+ provider?: string | null | undefined;
207
+ model?: string | null | undefined;
208
+ reasoningEffort?: "" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max" | null | undefined;
209
+ } & import("@deepseek-ai/cosmokit").Dict)[], Schemastery.ObjectT<{
210
+ provider: import("@deepseek-ai/schemastery").default<string, string>;
211
+ model: import("@deepseek-ai/schemastery").default<string, string>;
212
+ reasoningEffort: import("@deepseek-ai/schemastery").default<"" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max", "" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max">;
213
+ }>[]>;
214
+ }>, Schemastery.ObjectT<{
215
+ l1: import("@deepseek-ai/schemastery").default<({
216
+ provider?: string | null | undefined;
217
+ model?: string | null | undefined;
218
+ reasoningEffort?: "" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max" | null | undefined;
219
+ } & import("@deepseek-ai/cosmokit").Dict)[], Schemastery.ObjectT<{
220
+ provider: import("@deepseek-ai/schemastery").default<string, string>;
221
+ model: import("@deepseek-ai/schemastery").default<string, string>;
222
+ reasoningEffort: import("@deepseek-ai/schemastery").default<"" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max", "" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max">;
223
+ }>[]>;
224
+ l2: import("@deepseek-ai/schemastery").default<({
225
+ provider?: string | null | undefined;
226
+ model?: string | null | undefined;
227
+ reasoningEffort?: "" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max" | null | undefined;
228
+ } & import("@deepseek-ai/cosmokit").Dict)[], Schemastery.ObjectT<{
229
+ provider: import("@deepseek-ai/schemastery").default<string, string>;
230
+ model: import("@deepseek-ai/schemastery").default<string, string>;
231
+ reasoningEffort: import("@deepseek-ai/schemastery").default<"" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max", "" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max">;
232
+ }>[]>;
233
+ l3: import("@deepseek-ai/schemastery").default<({
234
+ provider?: string | null | undefined;
235
+ model?: string | null | undefined;
236
+ reasoningEffort?: "" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max" | null | undefined;
237
+ } & import("@deepseek-ai/cosmokit").Dict)[], Schemastery.ObjectT<{
238
+ provider: import("@deepseek-ai/schemastery").default<string, string>;
239
+ model: import("@deepseek-ai/schemastery").default<string, string>;
240
+ reasoningEffort: import("@deepseek-ai/schemastery").default<"" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max", "" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max">;
241
+ }>[]>;
242
+ }>>;
129
243
  maxTokens: import("@deepseek-ai/schemastery").default<number, number>;
130
244
  reasoningEffort: import("@deepseek-ai/schemastery").default<"" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max", "" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max">;
131
245
  temperature: import("@deepseek-ai/schemastery").default<number, number>;
@@ -240,6 +354,63 @@ export declare const Config: import("@deepseek-ai/schemastery").default<Schemast
240
354
  model: import("@deepseek-ai/schemastery").default<string, string>;
241
355
  reasoningEffort: import("@deepseek-ai/schemastery").default<"" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max", "" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max">;
242
356
  }>[]>;
357
+ layerRoutes: import("@deepseek-ai/schemastery").default<Schemastery.ObjectS<{
358
+ l1: import("@deepseek-ai/schemastery").default<({
359
+ provider?: string | null | undefined;
360
+ model?: string | null | undefined;
361
+ reasoningEffort?: "" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max" | null | undefined;
362
+ } & import("@deepseek-ai/cosmokit").Dict)[], Schemastery.ObjectT<{
363
+ provider: import("@deepseek-ai/schemastery").default<string, string>;
364
+ model: import("@deepseek-ai/schemastery").default<string, string>;
365
+ reasoningEffort: import("@deepseek-ai/schemastery").default<"" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max", "" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max">;
366
+ }>[]>;
367
+ l2: import("@deepseek-ai/schemastery").default<({
368
+ provider?: string | null | undefined;
369
+ model?: string | null | undefined;
370
+ reasoningEffort?: "" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max" | null | undefined;
371
+ } & import("@deepseek-ai/cosmokit").Dict)[], Schemastery.ObjectT<{
372
+ provider: import("@deepseek-ai/schemastery").default<string, string>;
373
+ model: import("@deepseek-ai/schemastery").default<string, string>;
374
+ reasoningEffort: import("@deepseek-ai/schemastery").default<"" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max", "" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max">;
375
+ }>[]>;
376
+ l3: import("@deepseek-ai/schemastery").default<({
377
+ provider?: string | null | undefined;
378
+ model?: string | null | undefined;
379
+ reasoningEffort?: "" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max" | null | undefined;
380
+ } & import("@deepseek-ai/cosmokit").Dict)[], Schemastery.ObjectT<{
381
+ provider: import("@deepseek-ai/schemastery").default<string, string>;
382
+ model: import("@deepseek-ai/schemastery").default<string, string>;
383
+ reasoningEffort: import("@deepseek-ai/schemastery").default<"" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max", "" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max">;
384
+ }>[]>;
385
+ }>, Schemastery.ObjectT<{
386
+ l1: import("@deepseek-ai/schemastery").default<({
387
+ provider?: string | null | undefined;
388
+ model?: string | null | undefined;
389
+ reasoningEffort?: "" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max" | null | undefined;
390
+ } & import("@deepseek-ai/cosmokit").Dict)[], Schemastery.ObjectT<{
391
+ provider: import("@deepseek-ai/schemastery").default<string, string>;
392
+ model: import("@deepseek-ai/schemastery").default<string, string>;
393
+ reasoningEffort: import("@deepseek-ai/schemastery").default<"" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max", "" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max">;
394
+ }>[]>;
395
+ l2: import("@deepseek-ai/schemastery").default<({
396
+ provider?: string | null | undefined;
397
+ model?: string | null | undefined;
398
+ reasoningEffort?: "" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max" | null | undefined;
399
+ } & import("@deepseek-ai/cosmokit").Dict)[], Schemastery.ObjectT<{
400
+ provider: import("@deepseek-ai/schemastery").default<string, string>;
401
+ model: import("@deepseek-ai/schemastery").default<string, string>;
402
+ reasoningEffort: import("@deepseek-ai/schemastery").default<"" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max", "" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max">;
403
+ }>[]>;
404
+ l3: import("@deepseek-ai/schemastery").default<({
405
+ provider?: string | null | undefined;
406
+ model?: string | null | undefined;
407
+ reasoningEffort?: "" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max" | null | undefined;
408
+ } & import("@deepseek-ai/cosmokit").Dict)[], Schemastery.ObjectT<{
409
+ provider: import("@deepseek-ai/schemastery").default<string, string>;
410
+ model: import("@deepseek-ai/schemastery").default<string, string>;
411
+ reasoningEffort: import("@deepseek-ai/schemastery").default<"" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max", "" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max">;
412
+ }>[]>;
413
+ }>>;
243
414
  maxTokens: import("@deepseek-ai/schemastery").default<number, number>;
244
415
  reasoningEffort: import("@deepseek-ai/schemastery").default<"" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max", "" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max">;
245
416
  temperature: import("@deepseek-ai/schemastery").default<number, number>;
@@ -257,6 +428,63 @@ export declare const Config: import("@deepseek-ai/schemastery").default<Schemast
257
428
  model: import("@deepseek-ai/schemastery").default<string, string>;
258
429
  reasoningEffort: import("@deepseek-ai/schemastery").default<"" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max", "" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max">;
259
430
  }>[]>;
431
+ layerRoutes: import("@deepseek-ai/schemastery").default<Schemastery.ObjectS<{
432
+ l1: import("@deepseek-ai/schemastery").default<({
433
+ provider?: string | null | undefined;
434
+ model?: string | null | undefined;
435
+ reasoningEffort?: "" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max" | null | undefined;
436
+ } & import("@deepseek-ai/cosmokit").Dict)[], Schemastery.ObjectT<{
437
+ provider: import("@deepseek-ai/schemastery").default<string, string>;
438
+ model: import("@deepseek-ai/schemastery").default<string, string>;
439
+ reasoningEffort: import("@deepseek-ai/schemastery").default<"" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max", "" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max">;
440
+ }>[]>;
441
+ l2: import("@deepseek-ai/schemastery").default<({
442
+ provider?: string | null | undefined;
443
+ model?: string | null | undefined;
444
+ reasoningEffort?: "" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max" | null | undefined;
445
+ } & import("@deepseek-ai/cosmokit").Dict)[], Schemastery.ObjectT<{
446
+ provider: import("@deepseek-ai/schemastery").default<string, string>;
447
+ model: import("@deepseek-ai/schemastery").default<string, string>;
448
+ reasoningEffort: import("@deepseek-ai/schemastery").default<"" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max", "" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max">;
449
+ }>[]>;
450
+ l3: import("@deepseek-ai/schemastery").default<({
451
+ provider?: string | null | undefined;
452
+ model?: string | null | undefined;
453
+ reasoningEffort?: "" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max" | null | undefined;
454
+ } & import("@deepseek-ai/cosmokit").Dict)[], Schemastery.ObjectT<{
455
+ provider: import("@deepseek-ai/schemastery").default<string, string>;
456
+ model: import("@deepseek-ai/schemastery").default<string, string>;
457
+ reasoningEffort: import("@deepseek-ai/schemastery").default<"" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max", "" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max">;
458
+ }>[]>;
459
+ }>, Schemastery.ObjectT<{
460
+ l1: import("@deepseek-ai/schemastery").default<({
461
+ provider?: string | null | undefined;
462
+ model?: string | null | undefined;
463
+ reasoningEffort?: "" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max" | null | undefined;
464
+ } & import("@deepseek-ai/cosmokit").Dict)[], Schemastery.ObjectT<{
465
+ provider: import("@deepseek-ai/schemastery").default<string, string>;
466
+ model: import("@deepseek-ai/schemastery").default<string, string>;
467
+ reasoningEffort: import("@deepseek-ai/schemastery").default<"" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max", "" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max">;
468
+ }>[]>;
469
+ l2: import("@deepseek-ai/schemastery").default<({
470
+ provider?: string | null | undefined;
471
+ model?: string | null | undefined;
472
+ reasoningEffort?: "" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max" | null | undefined;
473
+ } & import("@deepseek-ai/cosmokit").Dict)[], Schemastery.ObjectT<{
474
+ provider: import("@deepseek-ai/schemastery").default<string, string>;
475
+ model: import("@deepseek-ai/schemastery").default<string, string>;
476
+ reasoningEffort: import("@deepseek-ai/schemastery").default<"" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max", "" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max">;
477
+ }>[]>;
478
+ l3: import("@deepseek-ai/schemastery").default<({
479
+ provider?: string | null | undefined;
480
+ model?: string | null | undefined;
481
+ reasoningEffort?: "" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max" | null | undefined;
482
+ } & import("@deepseek-ai/cosmokit").Dict)[], Schemastery.ObjectT<{
483
+ provider: import("@deepseek-ai/schemastery").default<string, string>;
484
+ model: import("@deepseek-ai/schemastery").default<string, string>;
485
+ reasoningEffort: import("@deepseek-ai/schemastery").default<"" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max", "" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max">;
486
+ }>[]>;
487
+ }>>;
260
488
  maxTokens: import("@deepseek-ai/schemastery").default<number, number>;
261
489
  reasoningEffort: import("@deepseek-ai/schemastery").default<"" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max", "" | "off" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max">;
262
490
  temperature: import("@deepseek-ai/schemastery").default<number, number>;
package/dist/index.js CHANGED
@@ -307,6 +307,9 @@ export async function apply(ctx, config) {
307
307
  // 悬浮卡信息区数据源(session-stats 热路径端点;全部内存读 + 索引 COUNT,零文件 I/O)
308
308
  {
309
309
  recallStats: (sid) => recall.stats(sid),
310
+ memoryOccupancy: (sid) => recall.occupancy(sid),
311
+ profileEstimate: (sid) => recall.estimateProfileTokens(sid),
312
+ recallEstimate: (sid) => recall.estimateRecallTokens(sid),
310
313
  runnerView: (sid, mode) => runner.sessionView(sid, mode),
311
314
  l0Count: (sid) => stores.l0.countBySession(sid),
312
315
  capabilities: () => db.getCapabilities(),
package/dist/llm.d.ts CHANGED
@@ -25,21 +25,27 @@ export declare const LAYER_MAX_TOKENS_L2 = 32000;
25
25
  /** L3 画像(完整 persona 文档)。 */
26
26
  export declare const LAYER_MAX_TOKENS_L3 = 16000;
27
27
  /** 分层输出预算键(已迁入契约 src/contract.ts;import type 供本地使用,re-export 不断裂既有引用)。 */
28
- import type { DistillBudgetLayer } from './contract.js';
28
+ import type { DistillBudgetLayer, LayerRouteKey, StaticFallbackEntry } from './contract.js';
29
29
  export type { DistillBudgetLayer } from './contract.js';
30
30
  /** 各层内置默认预算(设置页"0 = 跟随默认"的默认值来源)。 */
31
31
  export declare const LAYER_DEFAULT_BUDGETS: Record<DistillBudgetLayer, number>;
32
- /**
33
- * 解析某蒸馏层的生效输出预算:运行时覆盖(cfg.llm.budgets,由 effectiveCfg 从
34
- * 设置页 distillBudgets 注入,0/缺省 = 跟随)→ 内置默认 → 思考档放大
35
- * (high/xhigh/max ×4,reasoning 计入输出预算的历史事故防线)。
36
- */
37
- export declare function resolveLayerTokens(cfg: {
32
+ /** resolveLayerTokens/layerEffortTrigger 需要的最小 cfg 视图(smoke 决策表用窄对象即可构造)。 */
33
+ export interface LayerRouteCfgView {
38
34
  llm: {
39
35
  reasoningEffort: string;
36
+ primaryEffort?: string;
37
+ layerRoutes?: Partial<Record<LayerRouteKey, StaticFallbackEntry[]>>;
38
+ layerChainsRuntime?: Partial<Record<LayerRouteKey, StaticFallbackEntry[]>>;
40
39
  budgets?: Partial<Record<DistillBudgetLayer, number>>;
41
40
  };
42
- }, layer: DistillBudgetLayer): number;
41
+ }
42
+ /**
43
+ * 解析某蒸馏层的生效输出预算:运行时覆盖(cfg.llm.budgets,由 effectiveCfg 从
44
+ * 设置页 distillBudgets 注入,0/缺省 = 跟随)→ 内置默认 → 思考档放大
45
+ * (high/xhigh/max ×4,reasoning 计入输出预算的历史事故防线)。放大触发档位
46
+ * 跟层走(#34 D8):层链头档位候选 > 全局主路由档位候选(primaryEffort > 静态全局)。
47
+ */
48
+ export declare function resolveLayerTokens(cfg: LayerRouteCfgView, layer: DistillBudgetLayer): number;
43
49
  /**
44
50
  * 高思考档集合(输出预算 ×4 的档位):阶段侧 layerMaxTokens 与 callLLM 的
45
51
  * 自动档防线共用同一张表——此前两处字面量表分叉(防线漏 xhigh),显式 xhigh
@@ -79,6 +85,20 @@ export declare function buildRouteChain(primary: {
79
85
  model: string;
80
86
  effort?: string;
81
87
  }, fallbacks: FallbackRouteEntry[] | undefined, globalEffort: string): DistillRoute[];
88
+ /** DistillLayer(调用点四键)→ 路由层键(三键):l1-extract/l1-dedup 同属 l1。 */
89
+ export declare function layerKeyFor(layer: DistillLayer): LayerRouteKey;
90
+ /** 该层的预算放大触发档位(D8):层链头档位候选 > 全局主路由档位候选(primaryEffort > 静态全局)。 */
91
+ export declare function layerEffortTrigger(cfg: LayerRouteCfgView, key: LayerRouteKey): string;
92
+ /**
93
+ * 解析某次蒸馏调用的实际路由链(callLLM 入口):有层标签且该层配了层链 → 层链
94
+ * 完整替换(buildRouteChain 复用:头行在前、条目去重、档位三级候选);否则现行
95
+ * 全局解析(主路由既有优先级 + fallbacks,语义一个比特不动)。layer 缺省
96
+ * (bench/测试缝)= 全局解析。
97
+ */
98
+ export declare function resolveLayerRoutes(ctx: Context, cfg: MemoryConfig, layer?: DistillLayer): Promise<DistillRoute[]>;
99
+ /** 层链解析的同步半边(llm-providers 视图与 resolveLayerRoutes 共用一条真值路径):
100
+ * 该层配了有效层链 → 完整链;null = 该层跟随全局解析。 */
101
+ export declare function layerChainOrNull(cfg: LayerRouteCfgView, key: LayerRouteKey): DistillRoute[] | null;
82
102
  export interface ModelEffortInfo {
83
103
  /** 模型可设置的思考档位 id(适配器声明;空 = 未声明/不可设置) */
84
104
  efforts: string[];
@@ -89,6 +109,12 @@ export interface ModelEffortInfo {
89
109
  export declare function invalidateEffortCache(): void;
90
110
  /** 探询某模型的思考档位能力;失败返回 null(调用方保持旧发送行为,不改判)。 */
91
111
  export declare function resolveModelEfforts(ctx: Context, provider: string, model: string): Promise<ModelEffortInfo | null>;
112
+ /**
113
+ * 探询某模型的上下文窗口容量(adapter 声明的 provider-owned capacity)。
114
+ * 与 effortCache 同源同失效策略(invalidateEffortCache 一并清空);
115
+ * 仅用于占用指示器的分母展示——分母必须与官方环同源(模型声明值),禁止 client 自估。
116
+ */
117
+ export declare function resolveModelContextWindow(ctx: Context, provider: string, model: string): Promise<number | null>;
92
118
  export type EffortDecisionReason = 'supported' | 'auto-default' | 'auto-high' | 'alias-none' | 'unsupported' | 'no-efforts' | 'no-capability';
93
119
  export interface EffortDecision {
94
120
  /** 实际发送的档位;'' = 不发送(跟随模型默认) */