lume-dsh-plugin 0.5.0 → 0.6.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.
Files changed (40) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +305 -305
  3. package/assets/personalities/butler-corpus.jsonl +0 -0
  4. package/assets/personalities/butler.txt +0 -0
  5. package/assets/personalities/loli-corpus.jsonl +30 -30
  6. package/assets/personalities/loli.txt +12 -12
  7. package/assets/personalities/none-corpus.jsonl +0 -0
  8. package/assets/personalities/none.txt +0 -0
  9. package/assets/personalities/senpai-corpus.jsonl +30 -30
  10. package/assets/personalities/senpai.txt +12 -12
  11. package/assets/personalities/tsundere-corpus.jsonl +0 -0
  12. package/assets/personalities/tsundere.txt +0 -0
  13. package/assets/personalities.json +47 -47
  14. package/cordis.patch.yml +5 -5
  15. package/lib/client.js +0 -0
  16. package/lib/core/card.js +0 -0
  17. package/lib/core/dialogue-mining.js +0 -0
  18. package/lib/core/leak-detector.js +0 -0
  19. package/lib/core/manifest.js +0 -0
  20. package/lib/core/persona-text.js +0 -0
  21. package/lib/core/retrieval.js +0 -0
  22. package/lib/core/sampling.js +0 -0
  23. package/lib/core/text.js +0 -0
  24. package/lib/host/boundary.js +0 -0
  25. package/lib/host/compaction.js +0 -0
  26. package/lib/host/diag.js +0 -0
  27. package/lib/host/distill.js +12 -12
  28. package/lib/host/extraction.js +0 -0
  29. package/lib/host/identity.js +0 -0
  30. package/lib/host/injection.js +0 -0
  31. package/lib/host/personalities.js +0 -0
  32. package/lib/host/protocol.js +52 -9
  33. package/lib/host/reflection.js +0 -0
  34. package/lib/host/registry.js +0 -0
  35. package/lib/host/rpc.js +0 -0
  36. package/lib/host/session-runtime.js +2 -0
  37. package/lib/host/store.js +0 -0
  38. package/lib/host/thinking.js +40 -40
  39. package/lib/index.js +135 -30
  40. package/package.json +112 -112
@@ -11,54 +11,54 @@
11
11
  * 计划/分解条款,只保留行为约束、证据纪律与事实边界
12
12
  */
13
13
  /** Codex 风格任务执行协议:完整版。 */
14
- export const THINKING_TEXT = `[任务执行协议]
15
-
16
- 你应遵循以下公开的工程工作协议。它约束任务如何被完成,不要求输出隐藏的逐步思考过程;对外只给出必要的结论、计划、变更和验证结果。
17
-
18
- **身份分工**:人设只影响自然语言表达;本协议负责正确完成任务。代码、数学、工具调用、结构化输出和安全判断保持准确、朴素,不因人设而戏剧化。
19
-
20
- **P0 上下文管理**:先确认用户真正要达成的结果、约束、涉及的文件/系统和完成标准。上下文变长时压缩为:目标、已完成事项、关键决策、当前状态、错误、已排除假设、下一步。不要反复提出已经解决或排除的问题。
21
-
22
- **P1 阶段门控**:复杂任务按“理解 → 只读调研 → 简短计划 → 执行 → 验证 → 汇报”推进。调研和计划阶段不修改外部状态;未确认目标文件、接口和影响范围前,不直接动手。
23
-
24
- **P1 任务分解**:把大任务拆成可验证的小步骤,优先处理阻塞项和高风险项。每一步都说明完成条件;能并行的只读检查并行进行,存在依赖的步骤按顺序执行。
25
-
26
- **P1 自适应投入**:不要把“快速”当成固定目标。简单、低风险、目标明确且可直接验证的问题,直接给出答案或执行最小步骤;复杂、模糊、高风险、涉及数据迁移/外部状态或验证成本高的问题,主动增加上下文分析、方案比较、边界检查和验证轮次。只有在信息足够且风险可控时才快速收敛。
27
-
28
- **P0 意图对齐**:先辨认这一轮是问答、查找、讨论、诊断还是执行。问答先回答;查找先核对事实;讨论先比较取舍;诊断先解释证据和根因,不越权修复;执行才修改状态。用户要结论时不要只汇报动作,用户要探讨时不要擅自锁定方案。
29
-
30
- **P1 信息路由**:优先定位最可能影响结果的入口、数据流和约束,不平均浏览无关内容;无依赖的只读检查可以并行,依赖前置结果的操作必须等待确认。
31
-
32
- **P2 变更纪律**:修改前完整读取相关文件,理解现有实现和用户已有改动;一次性完成同一文件的相关修改。保持改动最小、可回滚、与现有接口兼容,不重写无关代码,不覆盖用户数据。
33
-
34
- **P2 验证闭环**:每次修改后立即运行与风险匹配的测试、类型检查、构建或最小复现。不要只看“命令成功”,还要确认输出确实满足目标。发现失败先归因:输入、逻辑、接口、环境或权限;修复后重新验证。
35
-
36
- **P2 证据时效**:日志、历史记录、报错文本、旧结论都带时间。引用它们作为证据前先核对时间戳是否落在当前问题的时间窗口内,并确认因果关系——**历史里存在的错误不等于当前问题的原因**。日志里翻到一条报错就直接当成用户当前症状的解释,是最常见的误判。若无法确认时间归属,如实说明「这条是历史记录,与当前问题是否相关未确认」,再去找与当前时间窗对应的证据;找不到就说不确定,不要用旧错误填空。
37
-
38
- **P2 达成标准**:完成动作不等于达成目标。交付前必须回答:用户要的结果是否已经出现?用户能否实际使用?是否引入了需要用户清理的中间文件、配置、会话或其他副作用?
39
-
40
- **P2 振荡预防**:同一假设连续失败后停止重复尝试,记录失败原因并换方案。已排除的假设不再重提;不使用破坏性命令绕过问题;不把测试删掉或放宽断言来制造假成功。
41
-
42
- **P3 结果复核**:完成前逐项对照用户要求、边界条件、错误路径、兼容性和数据保留。区分“已实现”“已验证”“推测有效”和“仍然缺失”,不把部分完成说成全部完成。
43
-
44
- **工具与安全**:工具调用前判断是否只读、是否会写入或删除、目标是否精确、是否涉及隐私或外部通信。优先使用专用工具和最小权限;破坏性操作、敏感数据传输和不可逆变更必须先获得明确授权。
45
-
46
- **代码任务**:先定位入口、数据流和测试,再修改;优先复用现有抽象;为新行为补回归测试;同时考虑旧数据迁移、失败回退和用户已有状态。最终汇报修改文件、验证结果、已知限制和用户需要采取的动作。
47
-
48
- **对话任务**:先直接回答当前问题,再补充必要依据;简单问题保持简洁,复杂问题给出足够的推理依据、假设和验证边界。不编造已经执行的操作、工具结果、文件内容或当前状态。需要用户决定时只提出真正阻塞的问题。
49
-
50
- **隐私与事实边界**:示例、历史消息和角色记忆用于相关性与表达参考,不自动等于当前事实。涉及时间、地点、当前行为和现实状态时,只依据当前上下文或可靠工具结果。
51
-
14
+ export const THINKING_TEXT = `[任务执行协议]
15
+
16
+ 你应遵循以下公开的工程工作协议。它约束任务如何被完成,不要求输出隐藏的逐步思考过程;对外只给出必要的结论、计划、变更和验证结果。
17
+
18
+ **身份分工**:人设只影响自然语言表达;本协议负责正确完成任务。代码、数学、工具调用、结构化输出和安全判断保持准确、朴素,不因人设而戏剧化。
19
+
20
+ **P0 上下文管理**:先确认用户真正要达成的结果、约束、涉及的文件/系统和完成标准。上下文变长时压缩为:目标、已完成事项、关键决策、当前状态、错误、已排除假设、下一步。不要反复提出已经解决或排除的问题。
21
+
22
+ **P1 阶段门控**:复杂任务按“理解 → 只读调研 → 简短计划 → 执行 → 验证 → 汇报”推进。调研和计划阶段不修改外部状态;未确认目标文件、接口和影响范围前,不直接动手。
23
+
24
+ **P1 任务分解**:把大任务拆成可验证的小步骤,优先处理阻塞项和高风险项。每一步都说明完成条件;能并行的只读检查并行进行,存在依赖的步骤按顺序执行。
25
+
26
+ **P1 自适应投入**:不要把“快速”当成固定目标。简单、低风险、目标明确且可直接验证的问题,直接给出答案或执行最小步骤;复杂、模糊、高风险、涉及数据迁移/外部状态或验证成本高的问题,主动增加上下文分析、方案比较、边界检查和验证轮次。只有在信息足够且风险可控时才快速收敛。
27
+
28
+ **P0 意图对齐**:先辨认这一轮是问答、查找、讨论、诊断还是执行。问答先回答;查找先核对事实;讨论先比较取舍;诊断先解释证据和根因,不越权修复;执行才修改状态。用户要结论时不要只汇报动作,用户要探讨时不要擅自锁定方案。
29
+
30
+ **P1 信息路由**:优先定位最可能影响结果的入口、数据流和约束,不平均浏览无关内容;无依赖的只读检查可以并行,依赖前置结果的操作必须等待确认。
31
+
32
+ **P2 变更纪律**:修改前完整读取相关文件,理解现有实现和用户已有改动;一次性完成同一文件的相关修改。保持改动最小、可回滚、与现有接口兼容,不重写无关代码,不覆盖用户数据。
33
+
34
+ **P2 验证闭环**:每次修改后立即运行与风险匹配的测试、类型检查、构建或最小复现。不要只看“命令成功”,还要确认输出确实满足目标。发现失败先归因:输入、逻辑、接口、环境或权限;修复后重新验证。
35
+
36
+ **P2 证据时效**:日志、历史记录、报错文本、旧结论都带时间。引用它们作为证据前先核对时间戳是否落在当前问题的时间窗口内,并确认因果关系——**历史里存在的错误不等于当前问题的原因**。日志里翻到一条报错就直接当成用户当前症状的解释,是最常见的误判。若无法确认时间归属,如实说明「这条是历史记录,与当前问题是否相关未确认」,再去找与当前时间窗对应的证据;找不到就说不确定,不要用旧错误填空。
37
+
38
+ **P2 达成标准**:完成动作不等于达成目标。交付前必须回答:用户要的结果是否已经出现?用户能否实际使用?是否引入了需要用户清理的中间文件、配置、会话或其他副作用?
39
+
40
+ **P2 振荡预防**:同一假设连续失败后停止重复尝试,记录失败原因并换方案。已排除的假设不再重提;不使用破坏性命令绕过问题;不把测试删掉或放宽断言来制造假成功。
41
+
42
+ **P3 结果复核**:完成前逐项对照用户要求、边界条件、错误路径、兼容性和数据保留。区分“已实现”“已验证”“推测有效”和“仍然缺失”,不把部分完成说成全部完成。
43
+
44
+ **工具与安全**:工具调用前判断是否只读、是否会写入或删除、目标是否精确、是否涉及隐私或外部通信。优先使用专用工具和最小权限;破坏性操作、敏感数据传输和不可逆变更必须先获得明确授权。
45
+
46
+ **代码任务**:先定位入口、数据流和测试,再修改;优先复用现有抽象;为新行为补回归测试;同时考虑旧数据迁移、失败回退和用户已有状态。最终汇报修改文件、验证结果、已知限制和用户需要采取的动作。
47
+
48
+ **对话任务**:先直接回答当前问题,再补充必要依据;简单问题保持简洁,复杂问题给出足够的推理依据、假设和验证边界。不编造已经执行的操作、工具结果、文件内容或当前状态。需要用户决定时只提出真正阻塞的问题。
49
+
50
+ **隐私与事实边界**:示例、历史消息和角色记忆用于相关性与表达参考,不自动等于当前事实。涉及时间、地点、当前行为和现实状态时,只依据当前上下文或可靠工具结果。
51
+
52
52
  每次完成一个阶段后,检查:目标是否仍然一致?变更是否在授权范围内?验证是否覆盖了最可能的失败方式?`;
53
53
  /** 普通闲聊用短版协议;任务型请求才注入完整版,避免每轮重复支付完整工作协议。 */
54
- export const THINKING_COMPACT_TEXT = `[任务执行协议]
54
+ export const THINKING_COMPACT_TEXT = `[任务执行协议]
55
55
  先区分问答、查找、讨论、诊断、执行:问答先答,查找先核对,讨论先比较,诊断先归因,执行才改动。复杂或高风险任务先理解目标和约束,再调研、计划、执行、验证、复核。修改前读取相关内容,修改后确认实际生效并检查副作用;失败先归因,不重复已排除方案。引用日志、历史记录或旧报错作为证据时,先核对时间戳是否落在当前问题的时间窗口内——历史错误不等于当前问题的原因,无法确认就明说。人设只影响表达,不影响事实、代码、工具调用和安全判断。历史示例只参考风格,不自动等于当前事实。`;
56
56
  /** 任务型请求的判据:命中即注入完整协议(而非短版)。 */
57
57
  export const TASK_SIGNAL_RE = /代码|编程|文件|项目|仓库|脚本|命令|调研|研究|分析|实现|修改|修复|构建|测试|部署|配置|安装|迁移|导入|导出|接口|API|数据库|批量|计划|方案|风险|审查|review|debug|bug|深度|复杂/i;
58
58
  /** 推理型模型判据:命中即用精简协议(它天生会计划,重复条款只稀释注意力)。 */
59
59
  export const REASONING_MODEL_RE = /deepseek-v[345]|reason|o[134]|gpt-5/i;
60
60
  /** 推理型模型的任务协议:省掉它天生具备的计划/分解条款,保留行为约束与事实边界。 */
61
- export const THINKING_REASONING_TEXT = `[任务执行协议]
61
+ export const THINKING_REASONING_TEXT = `[任务执行协议]
62
62
  已确认当前模型具备推理能力。仍须保护用户改动,修改后立即验证;失败先归因并更换方案,不重复已排除假设;完成前复核需求、边界和数据保留。引用日志、历史记录或旧报错作为证据时先核对时间戳与因果:历史错误不等于当前问题的原因,确认不了就明说。示例、历史消息和角色记忆只作表达与相关性参考,不自动等于当前事实。人设只影响表达,不影响代码、工具调用和安全判断。`;
63
63
  /**
64
64
  * 按「是否任务型 × 是否推理模型」选择协议变体。
package/lib/index.js CHANGED
@@ -34,7 +34,7 @@ import { SessionRuntimeStore } from "./host/session-runtime.js";
34
34
  import { isCompactionCheckpoint } from "./host/compaction.js";
35
35
  import { LUME_REFLECTION_SPEC, ReflectionStore, buildReflectionPrompt, parseReflectionScore } from "./host/reflection.js";
36
36
  import { appendLumeLog } from "./host/diag.js";
37
- import { buildAlignmentCorrection, buildCompactionNotice, buildInteractionDirective, buildLongSessionGuard, buildSessionAnchor, buildTaskPhaseDirective, buildToolEvidenceDirective, classifyInteraction, taskPhaseForMode } from "./host/protocol.js";
37
+ import { advancePhase, buildAlignmentCorrection, buildCompactionNotice, buildInteractionDirective, buildLongSessionGuard, buildSessionAnchor, buildTaskPhaseDirective, buildToolFailureNotice, classifyInteraction, isUserAuthored, taskPhaseForMode } from "./host/protocol.js";
38
38
  import { REASONING_MODEL_RE, TASK_SIGNAL_RE, selectThinkingProtocol } from "./host/thinking.js";
39
39
  /** schemastery → domainTable 形参的桥接(与 identity.ts 同款)。 */
40
40
  const recordSchema = zodLike;
@@ -63,6 +63,10 @@ const LUME_THINKING_ORDER = 1;
63
63
  const LUME_PERSONA_ORDER = 10000;
64
64
  const LUME_BOUNDARY_SECTION = "lume:boundary";
65
65
  const LUME_BOUNDARY_ORDER = 10100;
66
+ /** 工具失败提示:走 runtime-context 通道(渲染成对话尾部的一条消息),
67
+ * 不进 system 串——避免会变的内容每步作废前缀缓存。 */
68
+ const LUME_TOOL_NOTICE_CONTEXT = "lume:tool-notice";
69
+ const LUME_TOOL_NOTICE_ORDER = 10150;
66
70
  const MAX_SESSIONS = 200;
67
71
  const SWITCH_BOUNDARY_TURNS = 2;
68
72
  /** Cordis 插件名 */
@@ -434,19 +438,17 @@ export function apply(ctx, config = {}) {
434
438
  appendLumeLog(`[${sid}] 检测到上下文压缩检查点(第 ${st.turnIndex} 轮)`);
435
439
  break;
436
440
  }
441
+ // 只有真实用户消息能定义本轮意图。宿主快照(@deepseek-ai/dsh-system-prompt)、
442
+ // 工作区指令(agent-instructions)、技能目录(skill-catalog)都经这条通道投递,
443
+ // 曾被当成用户发言:覆盖真实请求,并把模式从「执行」冲成「问答」。
444
+ if (!isUserAuthored(event.data))
445
+ break;
437
446
  const text = messageText(event.data);
438
447
  if (text) {
439
448
  const normalized = text.trim().replace(/\s+/g, " ").slice(0, 240);
440
449
  const explicitCorrection = /不是这个意思|不是我说的|你理解错|答非所问|听不懂|我说的是|我指的是|不对|错了|别这样|重新来/i.test(text);
441
450
  const repeatedRequest = normalized.length >= 5 && st.recentUserQueries.includes(normalized);
442
451
  st.userText = text;
443
- st.lastQuery = text;
444
- st.interactionMode = classifyInteraction(text);
445
- st.taskPhase = taskPhaseForMode(st.interactionMode);
446
- st.toolCalls = 0;
447
- st.toolSuccesses = 0;
448
- st.toolFailures = 0;
449
- st.toolUnknown = 0;
450
452
  st.alignmentCorrection = explicitCorrection
451
453
  ? buildAlignmentCorrection("user-correction")
452
454
  : repeatedRequest
@@ -458,6 +460,8 @@ export function apply(ctx, config = {}) {
458
460
  st.recentTurns.push(`用户: ${text.slice(0, 300)}`);
459
461
  if (st.recentTurns.length > 12)
460
462
  st.recentTurns.shift();
463
+ // 模式/阶段的冻结统一由 resolveIntent(组装时读会话权威历史)负责,
464
+ // 这里只记账:两处都写会让「同一条消息」被判定为不同轮而反复重算。
461
465
  }
462
466
  break;
463
467
  }
@@ -474,7 +478,7 @@ export function apply(ctx, config = {}) {
474
478
  case "tool/call": {
475
479
  st.toolCalls++;
476
480
  if (st.interactionMode === "execute")
477
- st.taskPhase = "execute";
481
+ st.taskPhase = advancePhase(st.taskPhase, "execute");
478
482
  break;
479
483
  }
480
484
  case "tool/result": {
@@ -489,7 +493,7 @@ export function apply(ctx, config = {}) {
489
493
  else
490
494
  st.toolSuccesses++;
491
495
  if (st.interactionMode === "execute")
492
- st.taskPhase = unknownResult || explicitError ? "diagnose" : "verify";
496
+ st.taskPhase = advancePhase(st.taskPhase, unknownResult || explicitError ? "diagnose" : "verify");
493
497
  break;
494
498
  }
495
499
  case "compaction/summary": {
@@ -525,7 +529,7 @@ export function apply(ctx, config = {}) {
525
529
  ? "〔上轮交付复核〕上一轮执行回复没有给出可见的验证证据。本轮若继续处理同一任务,先确认上轮变更是否真实生效,再继续扩大范围。"
526
530
  : null;
527
531
  if (st.interactionMode === "execute")
528
- st.taskPhase = st.toolFailures > 0 || st.toolUnknown > 0 ? "diagnose" : claimsVerification ? "deliver" : "verify";
532
+ st.taskPhase = advancePhase(st.taskPhase, st.toolFailures > 0 || st.toolUnknown > 0 ? "diagnose" : claimsVerification ? "deliver" : "verify");
529
533
  // 即时对齐只影响当前轮;下一轮重新根据用户消息判断,避免纠偏条款滞留。
530
534
  st.alignmentCorrection = null;
531
535
  // 风格泄漏检测挂在 turn/end(该事件已被窗口机制验证可靠;assistant/message
@@ -654,7 +658,50 @@ export function apply(ctx, config = {}) {
654
658
  }));
655
659
  }, "lume: persona tools");
656
660
  // ── 人设五段式注入 + 切换播报 ──
657
- function buildSessionText(sid) {
661
+ /**
662
+ * 权威意图解析:与宿主会话历史对账,而不是只信事件缓存。
663
+ *
664
+ * 事件投递与首次提示词组装存在时序差——实测同一轮的 step 1 仍带上一轮的模式,
665
+ * step 2 才切到本轮分类,系统提示词因此在轮内变化、前缀缓存每步作废。这里直接
666
+ * 读会话投影出的消息(`deriveMessages`,宿主侧带增量缓存),取最后一条真实用户
667
+ * 消息作为本轮意图;messageId 未变时零成本短路,保证一轮内只冻结一次。
668
+ */
669
+ function resolveIntent(context, st) {
670
+ const session = context?.agent?.session;
671
+ const messages = typeof session?.deriveMessages === "function" ? session.deriveMessages() : [];
672
+ let text = null;
673
+ let messageId = "";
674
+ for (let i = messages.length - 1; i >= 0; i--) {
675
+ const msg = messages[i];
676
+ if (!isUserAuthored(msg))
677
+ continue;
678
+ const candidate = messageText(msg);
679
+ if (!candidate)
680
+ continue;
681
+ text = candidate;
682
+ messageId = String(msg.id ?? "");
683
+ break;
684
+ }
685
+ // 权威会话历史不可用(旧宿主、非标准 session 对象)时退回事件记录的用户文本:
686
+ // 否则意图会永远停在默认的「问答」,比改动前更差。
687
+ if (text === null && st.userText) {
688
+ text = st.userText;
689
+ messageId = `text:${st.userText.slice(0, 120)}`;
690
+ }
691
+ if (text !== null && (st.intent === null || st.intent.messageId !== messageId)) {
692
+ const mode = classifyInteraction(text);
693
+ st.intent = { turnIndex: st.turnIndex, messageId, text };
694
+ st.lastQuery = text;
695
+ st.interactionMode = mode;
696
+ st.taskPhase = taskPhaseForMode(mode);
697
+ st.toolCalls = 0;
698
+ st.toolSuccesses = 0;
699
+ st.toolFailures = 0;
700
+ st.toolUnknown = 0;
701
+ }
702
+ return { text: st.intent?.text ?? st.lastQuery ?? "", mode: st.interactionMode };
703
+ }
704
+ function buildSessionText(sid, context) {
658
705
  if (!currentStore)
659
706
  return "";
660
707
  const st = runtime.get(sid);
@@ -680,18 +727,49 @@ export function apply(ctx, config = {}) {
680
727
  : null;
681
728
  // 播报改由独立的尾部 section 渲染(LUME_BOUNDARY_SECTION),人设段不再内联
682
729
  st.activeBoundary = boundaryText;
683
- const text = buildPersonaSection({
684
- persona,
685
- profileName: personaName ? registry.profileNameOf(personaName) : null,
686
- memories: personaName ? identity?.getMemory(personaName) ?? [] : [],
687
- styleRules: personaName ? identity?.getStyleRules(personaName) ?? [] : [],
688
- corpusPins: personaName ? identity?.getCorpusPins(personaName) ?? [] : [],
689
- query: st.lastQuery,
690
- turnIndex: st.turnIndex,
691
- sessionKey: sid,
692
- boundaryText: null,
693
- config: { sampleCount, sampleMin, memoryInject, styleInject, strategy },
694
- });
730
+ const memories = personaName ? identity?.getMemory(personaName) ?? [] : [];
731
+ const styleRules = personaName ? identity?.getStyleRules(personaName) ?? [] : [];
732
+ const corpusPins = personaName ? identity?.getCorpusPins(personaName) ?? [] : [];
733
+ const { text: query } = resolveIntent(context, st);
734
+ // 一轮一算:注入段按查询做 top-k 检索,查询或记忆每步抖动会让整段改写,
735
+ // 系统提示词随之在轮内变化(实测一轮产生 28 份),前缀缓存每步作废。
736
+ // 指纹覆盖 persona、查询、记忆/风格/语料与注入配置;人设切换、边界窗口、
737
+ // 记忆写入都会改变指纹而自动失效。
738
+ const last = (list, field) => list.length > 0 ? list[list.length - 1]?.[field] : "";
739
+ const key = [
740
+ String(personaName),
741
+ String(persona?.name ?? ""),
742
+ query,
743
+ String(st.turnIndex),
744
+ String(memories.length),
745
+ String(last(memories, "text")),
746
+ String(styleRules.length),
747
+ String(last(styleRules, "rule")),
748
+ String(corpusPins.length),
749
+ String(last(corpusPins, "assistant")),
750
+ String(boundaryText ?? ""),
751
+ String(sampleCount),
752
+ String(sampleMin),
753
+ String(memoryInject),
754
+ String(styleInject),
755
+ strategy,
756
+ ].join("\u0001");
757
+ let text = st.personaCache !== null && st.personaCache.key === key ? st.personaCache.text : null;
758
+ if (text === null) {
759
+ text = buildPersonaSection({
760
+ persona,
761
+ profileName: personaName ? registry.profileNameOf(personaName) : null,
762
+ memories,
763
+ styleRules,
764
+ corpusPins,
765
+ query,
766
+ turnIndex: st.turnIndex,
767
+ sessionKey: sid,
768
+ boundaryText: null,
769
+ config: { sampleCount, sampleMin, memoryInject, styleInject, strategy },
770
+ });
771
+ st.personaCache = { turnIndex: st.turnIndex, key, text };
772
+ }
695
773
  st.lastInjected = personaName;
696
774
  if (greeting)
697
775
  st.switchGreetingPending = false;
@@ -732,7 +810,7 @@ export function apply(ctx, config = {}) {
732
810
  order: personaOrder,
733
811
  text: (context) => {
734
812
  const sid = context.agent?.session?.id ?? context.agent?.id;
735
- return sid ? buildSessionText(String(sid)) : "";
813
+ return sid ? buildSessionText(String(sid), context) : "";
736
814
  },
737
815
  }), "lume.persona-section()");
738
816
  ctx.effect(() => ctx.systemPrompt.section({
@@ -751,7 +829,10 @@ export function apply(ctx, config = {}) {
751
829
  text: (context) => {
752
830
  const sid = context.agent?.session?.id ?? context.agent?.id;
753
831
  const st = sid ? runtime.get(String(sid)) : null;
754
- const query = st?.lastQuery ?? "";
832
+ // 意图取自会话权威历史(resolveIntent),不再读事件缓存里的最新文本:
833
+ // 后者会被注入消息覆盖,且首步可能还带着上一轮的值,导致模式在轮内漂移。
834
+ const intent = st !== null && sid ? resolveIntent(context, st) : null;
835
+ const query = intent?.text ?? "";
755
836
  // 已知推理型模型具备计划能力,任务轮只保留变更、验证、归因和复核约束;
756
837
  // 路由未知时使用完整版,避免误判造成能力退化。
757
838
  const base = selectThinkingProtocol({
@@ -762,17 +843,41 @@ export function apply(ctx, config = {}) {
762
843
  // 才能在「不使用人设」的纯任务会话里也生效。
763
844
  const correction = st?.protocolCorrection;
764
845
  const reflectionHint = reflectionStore?.getFeedback() ?? null;
765
- const route = buildInteractionDirective(st?.interactionMode ?? "question");
846
+ const mode = intent?.mode ?? "question";
847
+ const route = buildInteractionDirective(mode);
766
848
  const longSession = buildLongSessionGuard(st?.turnIndex ?? 0);
767
- const anchor = buildSessionAnchor(st?.turnIndex ?? 0, st?.interactionMode ?? "question", query, st?.recentTurns ?? []);
849
+ const anchor = buildSessionAnchor(st?.turnIndex ?? 0, mode, query, st?.recentTurns ?? []);
768
850
  const alignment = st?.alignmentCorrection;
769
851
  const postTurnReview = st?.postTurnReview;
770
852
  const phase = buildTaskPhaseDirective(st?.taskPhase ?? "answer");
771
- const toolEvidence = st ? buildToolEvidenceDirective({ calls: st.toolCalls, successes: st.toolSuccesses, failures: st.toolFailures, unknown: st.toolUnknown }) : null;
853
+ // 工具失败提示已移到 runtime-context 通道(见 lume.tool-notice-context):
854
+ // 它一出现就固定不变,但会变的内容不该待在 system 串里——那会作废前缀缓存。
772
855
  // 压缩重锚:宿主的 preset 隔离域负责压缩,Lume 只能观察事件;
773
856
  // 在压缩后一轮提醒「摘要不是完整历史」。
774
857
  const compactionNotice = st?.compaction ? buildCompactionNotice(st.compaction, st.turnIndex) : null;
775
- return [base, route, phase, longSession, anchor, alignment, postTurnReview, toolEvidence, compactionNotice, correction, reflectionHint].filter(Boolean).join("\n\n");
858
+ return [base, route, phase, longSession, anchor, alignment, postTurnReview, compactionNotice, correction, reflectionHint].filter(Boolean).join("\n\n");
776
859
  },
777
860
  }), "lume.thinking-section()");
861
+ // 工具失败提示走 runtime-context 通道:宿主把它渲染成对话尾部的一条消息,
862
+ // 而不是拼进 system 串。system 串只要变化就会写一条新的 request/header,
863
+ // 既在界面上多出一行「系统提示词」,也让前缀缓存从系统提示词处整段失效。
864
+ // `systemPrompt.context` 是宿主较新版本才有的 API,缺失时静默跳过(旧宿主下
865
+ // 只是失去这条提示,不影响其余功能)。
866
+ ctx.effect(() => {
867
+ if (typeof ctx.systemPrompt?.context !== "function") {
868
+ ctx.logger?.warn?.("lume: 当前宿主不支持 systemPrompt.context,工具失败提示已跳过(不影响其余功能)");
869
+ return;
870
+ }
871
+ return ctx.systemPrompt.context({
872
+ name: LUME_TOOL_NOTICE_CONTEXT,
873
+ order: LUME_TOOL_NOTICE_ORDER,
874
+ text: (context) => {
875
+ const sid = context.agent?.session?.id ?? context.agent?.id;
876
+ const st = sid ? runtime.get(String(sid)) : null;
877
+ if (!st)
878
+ return "";
879
+ return buildToolFailureNotice({ failures: st.toolFailures, unknown: st.toolUnknown }) ?? "";
880
+ },
881
+ });
882
+ }, "lume.tool-notice-context()");
778
883
  }
package/package.json CHANGED
@@ -1,112 +1,112 @@
1
- {
2
- "name": "lume-dsh-plugin",
3
- "description": "微光 (Lume) — DSH Desktop 增强插件:Codex 风格自适应任务执行协议 + 人设系统(聊天记录蒸馏具名角色、长期记忆、风格纠偏自动捕获)",
4
- "version": "0.5.0",
5
- "type": "module",
6
- "main": "lib/index.js",
7
- "exports": {
8
- ".": {
9
- "default": "./lib/index.js"
10
- },
11
- "./client": {
12
- "default": "./lib/client.js"
13
- },
14
- "./package.json": "./package.json"
15
- },
16
- "dsh": {
17
- "bundle": {
18
- "patch": "./cordis.patch.yml"
19
- },
20
- "client": {
21
- "inject": [
22
- "@deepseek-ai/dsh-client-connection",
23
- "@deepseek-ai/dsh-client-locale",
24
- "@deepseek-ai/dsh-client-runtime",
25
- "@deepseek-ai/dsh-client-ui-conversation",
26
- "@deepseek-ai/dsh-client-ui-primitives"
27
- ],
28
- "external": [
29
- "react",
30
- "react/jsx-runtime",
31
- "@deepseek-ai/dsh-client-ui-primitives"
32
- ],
33
- "platform": "web",
34
- "immediately": true
35
- }
36
- },
37
- "license": "MIT",
38
- "repository": {
39
- "type": "git",
40
- "url": "https://github.com/cayan0x/Lume.git"
41
- },
42
- "keywords": [
43
- "dsh-plugin",
44
- "persona",
45
- "personality",
46
- "identity",
47
- "memory"
48
- ],
49
- "engines": {
50
- "node": ">=20"
51
- },
52
- "peerDependencies": {
53
- "@deepseek-ai/cordis": "^4.0.1",
54
- "@deepseek-ai/dsh-client-ui-primitives": "*",
55
- "@deepseek-ai/dsh-llm": "*",
56
- "@deepseek-ai/dsh-storage-domain": "*",
57
- "@deepseek-ai/dsh-tools": "*",
58
- "@deepseek-ai/schemastery": "^3.18.1"
59
- },
60
- "scripts": {
61
- "build": "tsc -p tsconfig.build.json && tsdown",
62
- "bundle": "tsdown",
63
- "watch": "tsdown --watch",
64
- "test": "vitest run"
65
- },
66
- "devDependencies": {
67
- "@deepseek-ai/cordis": "^4.0.1",
68
- "@deepseek-ai/dsh-agent-default-model": "^0.1.0-rc.6",
69
- "@deepseek-ai/dsh-client-ui-primitives": "^0.1.1-rc.2",
70
- "@deepseek-ai/dsh-llm": "^0.0.1-rc.1",
71
- "@deepseek-ai/dsh-scope": "^0.0.1-rc.1",
72
- "@deepseek-ai/dsh-session": "^0.0.1-rc.1",
73
- "@deepseek-ai/dsh-storage": "^0.0.1-rc.1",
74
- "@deepseek-ai/dsh-storage-domain": "^0.0.1-rc.1",
75
- "@deepseek-ai/dsh-storage-json": "^0.0.1-rc.1",
76
- "@deepseek-ai/dsh-timeout": "^0.0.1-rc.1",
77
- "@deepseek-ai/dsh-tools": "^0.0.1-rc.1",
78
- "@deepseek-ai/schemastery": "^3.18.1",
79
- "@types/node": "^22.20.1",
80
- "@types/react": "~18.3.1",
81
- "tsdown": "^0.22.14",
82
- "typescript": "^5.9.3",
83
- "vitest": "^3.2.7"
84
- },
85
- "files": [
86
- "lib",
87
- "cordis.patch.yml",
88
- "assets/personalities.json",
89
- "assets/personalities/*.txt",
90
- "assets/personalities/*.jsonl"
91
- ],
92
- "peerDependenciesMeta": {
93
- "@deepseek-ai/cordis": {
94
- "optional": true
95
- },
96
- "@deepseek-ai/dsh-client-ui-primitives": {
97
- "optional": true
98
- },
99
- "@deepseek-ai/dsh-llm": {
100
- "optional": true
101
- },
102
- "@deepseek-ai/dsh-storage-domain": {
103
- "optional": true
104
- },
105
- "@deepseek-ai/dsh-tools": {
106
- "optional": true
107
- },
108
- "@deepseek-ai/schemastery": {
109
- "optional": true
110
- }
111
- }
112
- }
1
+ {
2
+ "name": "lume-dsh-plugin",
3
+ "description": "微光 (Lume) — DSH Desktop 增强插件:Codex 风格自适应任务执行协议 + 人设系统(聊天记录蒸馏具名角色、长期记忆、风格纠偏自动捕获)",
4
+ "version": "0.6.0",
5
+ "type": "module",
6
+ "main": "lib/index.js",
7
+ "exports": {
8
+ ".": {
9
+ "default": "./lib/index.js"
10
+ },
11
+ "./client": {
12
+ "default": "./lib/client.js"
13
+ },
14
+ "./package.json": "./package.json"
15
+ },
16
+ "dsh": {
17
+ "bundle": {
18
+ "patch": "./cordis.patch.yml"
19
+ },
20
+ "client": {
21
+ "inject": [
22
+ "@deepseek-ai/dsh-client-connection",
23
+ "@deepseek-ai/dsh-client-locale",
24
+ "@deepseek-ai/dsh-client-runtime",
25
+ "@deepseek-ai/dsh-client-ui-conversation",
26
+ "@deepseek-ai/dsh-client-ui-primitives"
27
+ ],
28
+ "external": [
29
+ "react",
30
+ "react/jsx-runtime",
31
+ "@deepseek-ai/dsh-client-ui-primitives"
32
+ ],
33
+ "platform": "web",
34
+ "immediately": true
35
+ }
36
+ },
37
+ "license": "MIT",
38
+ "repository": {
39
+ "type": "git",
40
+ "url": "https://github.com/cayan0x/Lume.git"
41
+ },
42
+ "keywords": [
43
+ "dsh-plugin",
44
+ "persona",
45
+ "personality",
46
+ "identity",
47
+ "memory"
48
+ ],
49
+ "engines": {
50
+ "node": ">=20"
51
+ },
52
+ "peerDependencies": {
53
+ "@deepseek-ai/cordis": "^4.0.1",
54
+ "@deepseek-ai/dsh-client-ui-primitives": "*",
55
+ "@deepseek-ai/dsh-llm": "*",
56
+ "@deepseek-ai/dsh-storage-domain": "*",
57
+ "@deepseek-ai/dsh-tools": "*",
58
+ "@deepseek-ai/schemastery": "^3.18.1"
59
+ },
60
+ "scripts": {
61
+ "build": "tsc -p tsconfig.build.json && tsdown",
62
+ "bundle": "tsdown",
63
+ "watch": "tsdown --watch",
64
+ "test": "vitest run"
65
+ },
66
+ "devDependencies": {
67
+ "@deepseek-ai/cordis": "^4.0.1",
68
+ "@deepseek-ai/dsh-agent-default-model": "^0.1.0-rc.6",
69
+ "@deepseek-ai/dsh-client-ui-primitives": "^0.1.1-rc.2",
70
+ "@deepseek-ai/dsh-llm": "^0.0.1-rc.1",
71
+ "@deepseek-ai/dsh-scope": "^0.0.1-rc.1",
72
+ "@deepseek-ai/dsh-session": "^0.0.1-rc.1",
73
+ "@deepseek-ai/dsh-storage": "^0.0.1-rc.1",
74
+ "@deepseek-ai/dsh-storage-domain": "^0.0.1-rc.1",
75
+ "@deepseek-ai/dsh-storage-json": "^0.0.1-rc.1",
76
+ "@deepseek-ai/dsh-timeout": "^0.0.1-rc.1",
77
+ "@deepseek-ai/dsh-tools": "^0.0.1-rc.1",
78
+ "@deepseek-ai/schemastery": "^3.18.1",
79
+ "@types/node": "^22.20.1",
80
+ "@types/react": "~18.3.1",
81
+ "tsdown": "^0.22.14",
82
+ "typescript": "^5.9.3",
83
+ "vitest": "^3.2.7"
84
+ },
85
+ "files": [
86
+ "lib",
87
+ "cordis.patch.yml",
88
+ "assets/personalities.json",
89
+ "assets/personalities/*.txt",
90
+ "assets/personalities/*.jsonl"
91
+ ],
92
+ "peerDependenciesMeta": {
93
+ "@deepseek-ai/cordis": {
94
+ "optional": true
95
+ },
96
+ "@deepseek-ai/dsh-client-ui-primitives": {
97
+ "optional": true
98
+ },
99
+ "@deepseek-ai/dsh-llm": {
100
+ "optional": true
101
+ },
102
+ "@deepseek-ai/dsh-storage-domain": {
103
+ "optional": true
104
+ },
105
+ "@deepseek-ai/dsh-tools": {
106
+ "optional": true
107
+ },
108
+ "@deepseek-ai/schemastery": {
109
+ "optional": true
110
+ }
111
+ }
112
+ }