memory-eternal 0.14.3 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/capture.js CHANGED
@@ -190,7 +190,7 @@ function cleanTitleLine(line) {
190
190
  * @param llm - dsh 的 llm 服务(ctx.get('llm'))
191
191
  * @param route - { provider, model } 解析结果
192
192
  * @param conversation - 本轮对话文本(用户+助手)
193
- * @param opts - { maxTokens, signal, existing }:existing 为 [{path,title,summary}]
193
+ * @param opts - { maxTokens, signal, existing, sessionId }:existing 为 [{path,title,summary}];sessionId 透传给 LLM(issue #51)
194
194
  * @returns Promise<null | { save:boolean } | { save:true, title, kind, tags, body } | { append_to, update }>
195
195
  */
196
196
  export async function summarizeTurn(llm, route, conversation, opts = {}) {
@@ -225,6 +225,24 @@ export function isEffortUnsupported(provider, model) { return effortRejected.has
225
225
  /** 仅供测试:清空记忆。 */
226
226
  export function resetEffortUnsupported() { effortRejected.clear() }
227
227
 
228
+ /**
229
+ * LLM 请求的会话标识(issue #51)。
230
+ *
231
+ * 为什么必须有:DSH 的 GenerateOptions 里有官方字段 `sessionId`,opencode-go 之类的网关
232
+ * **只在拿到它时**才注入 `x-opencode-session` 头;缺了这个头,网关一律 400
233
+ * `{"type":"MissingSessionID"}` —— 表现就是每一轮自动沉淀的首跳必然失败、白烧一次调用,
234
+ * 面板长期挂「⚠ 自动沉淀异常」(卡片其实靠换 provider 兜底建出来了)。
235
+ *
236
+ * 取值优先级:调用方透传的真实会话 id(同一会话的蒸馏可被网关按会话归并)→ 稳定常量
237
+ * (网关只要求"有值",同值同样能复用路由;不要每次生成随机值,否则等于没有会话亲和)。
238
+ */
239
+ const CAPTURE_LLM_SESSION_ID = 'memory-eternal-capture'
240
+ const REFINE_LLM_SESSION_ID = 'memory-eternal-refine'
241
+ export function resolveLlmSessionId(sessionId, fallback) {
242
+ const s = String(sessionId ?? '').trim()
243
+ return s || String(fallback || CAPTURE_LLM_SESSION_ID)
244
+ }
245
+
228
246
  /**
229
247
  * 单次 JSON 调用(把「发请求 → 收 finish → 抽文本」这条链复用给非沉淀场景,如矛盾判别)。
230
248
  * 与 summarizeTurnDetailed 共享同一套兼容处理:失败经 finish 暴露(不静默成"无输出")、
@@ -232,7 +250,7 @@ export function resetEffortUnsupported() { effortRejected.clear() }
232
250
  *
233
251
  * @returns {Promise<{text?:string, failure?:{code:string,message:string}}>}
234
252
  */
235
- export async function callLlmJson(llm, route, { system = '', user = '', maxTokens = 1500, purpose = 'memory-refine', signal } = {}) {
253
+ export async function callLlmJson(llm, route, { system = '', user = '', maxTokens = 1500, purpose = 'memory-refine', signal, sessionId } = {}) {
236
254
  const { BlockAssembler, createUserMessage } = await importLlmHelpers()
237
255
  const messages = [createUserMessage({
238
256
  content: [{ type: 'text', text: String(user || '') }],
@@ -249,6 +267,8 @@ export async function callLlmJson(llm, route, { system = '', user = '', maxToken
249
267
  maxTokens,
250
268
  ...(effort ? { reasoningEffort: effort } : {}),
251
269
  purpose,
270
+ // issue #51:网关(opencode-go 等)靠它注入 x-opencode-session;缺了首跳必 400。
271
+ sessionId: resolveLlmSessionId(sessionId, REFINE_LLM_SESSION_ID),
252
272
  signal: signal ?? AbortSignal.timeout(60000),
253
273
  }
254
274
  const runOnce = async (o) => {
@@ -324,6 +344,9 @@ export async function summarizeTurnDetailed(llm, route, conversation, opts = {})
324
344
  maxTokens,
325
345
  ...(effort ? { reasoningEffort: effort } : {}),
326
346
  purpose: 'memory-capture',
347
+ // issue #51:透传真实会话 id(拿不到就退稳定常量)。缺这个字段时 opencode-go 的蒸馏
348
+ // 首跳会 400 MissingSessionID,每轮白烧一次调用 + 面板误报「自动沉淀异常」。
349
+ sessionId: resolveLlmSessionId(opts.sessionId, CAPTURE_LLM_SESSION_ID),
327
350
  signal: opts.signal ?? AbortSignal.timeout(45000),
328
351
  }
329
352
  const runOnce = async (streamOptions) => {