@modusensus/dsh-mneme 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.
- package/README.md +3 -4
- package/lib/api.js +1 -1
- package/lib/client.js +353 -163
- package/lib/continuity.js +1 -1
- package/lib/dream/sleep.js +4 -4
- package/lib/dream.js +6 -6
- package/lib/entities/extractor.js +2 -2
- package/lib/peak-hours.js +117 -117
- package/lib/summarize.js +2 -2
- package/package.json +1 -1
- package/src/api.js +1 -1
- package/src/continuity.js +1 -1
- package/src/dream/sleep.js +4 -4
- package/src/dream.js +6 -6
- package/src/entities/extractor.js +2 -2
- package/src/peak-hours.js +117 -117
- package/src/summarize.js +2 -2
- package/test/api.test.js +2 -1
- package/test/client.test.js +17 -6
- package/test/continuity.test.js +1 -1
- package/test/llm-message-source.test.js +32 -2
package/lib/continuity.js
CHANGED
|
@@ -205,7 +205,7 @@ export function createContinuityRescue(ctx, store) {
|
|
|
205
205
|
...decision,
|
|
206
206
|
messages: [
|
|
207
207
|
...messages,
|
|
208
|
-
createUserMessage({ content: [{ type: "text", text }], source: { kind: "plugin", plugin: "dsh-mneme" } })
|
|
208
|
+
createUserMessage({ content: [{ type: "text", text }], source: { kind: "plugin:dsh-mneme", plugin: "dsh-mneme" } })
|
|
209
209
|
]
|
|
210
210
|
};
|
|
211
211
|
});
|
package/lib/dream/sleep.js
CHANGED
|
@@ -334,8 +334,8 @@ async function phaseConflicts(ctx, service, config, logger, runId, semantic = nu
|
|
|
334
334
|
maxTokens: config.sleepMaxTokens ?? 2048,
|
|
335
335
|
...(withEffort && sleepEffort ? { reasoningEffort: sleepEffort } : {}),
|
|
336
336
|
messages: [
|
|
337
|
-
{ role: "system", content: [{ type: "text", text: conflictPrompt }], source: { kind: "plugin", plugin: "dsh-mneme" } },
|
|
338
|
-
{ role: "user", content: [{ type: "text", text: listText }], source: { kind: "plugin", plugin: "dsh-mneme" } }
|
|
337
|
+
{ role: "system", content: [{ type: "text", text: conflictPrompt }], source: { kind: "plugin:dsh-mneme", plugin: "dsh-mneme" } },
|
|
338
|
+
{ role: "user", content: [{ type: "text", text: listText }], source: { kind: "plugin:dsh-mneme", plugin: "dsh-mneme" } }
|
|
339
339
|
]
|
|
340
340
|
}, reportUsage, (reason) => { conflictStreamFailure = describeStreamFailure(reason); }));
|
|
341
341
|
};
|
|
@@ -496,8 +496,8 @@ async function phasePatterns(ctx, service, config, logger, runId, signal = null)
|
|
|
496
496
|
maxTokens: config.sleepMaxTokens ?? 2048,
|
|
497
497
|
...(withEffort && sleepEffort ? { reasoningEffort: sleepEffort } : {}),
|
|
498
498
|
messages: [
|
|
499
|
-
{ role: "system", content: [{ type: "text", text: STR.prompts.pattern[language].replace("N", String(maxPatterns)) }], source: { kind: "plugin", plugin: "dsh-mneme" } },
|
|
500
|
-
{ role: "user", content: [{ type: "text", text: listText }], source: { kind: "plugin", plugin: "dsh-mneme" } }
|
|
499
|
+
{ role: "system", content: [{ type: "text", text: STR.prompts.pattern[language].replace("N", String(maxPatterns)) }], source: { kind: "plugin:dsh-mneme", plugin: "dsh-mneme" } },
|
|
500
|
+
{ role: "user", content: [{ type: "text", text: listText }], source: { kind: "plugin:dsh-mneme", plugin: "dsh-mneme" } }
|
|
501
501
|
]
|
|
502
502
|
}, reportUsage, (reason) => { patternStreamFailure = describeStreamFailure(reason); }));
|
|
503
503
|
};
|
package/lib/dream.js
CHANGED
|
@@ -422,8 +422,8 @@ async function generateNarratives({ ctx, service, config, route, language, effor
|
|
|
422
422
|
maxTokens: config.dreamMaxTokens ?? 2048,
|
|
423
423
|
...(withEffort && effort ? { reasoningEffort: effort } : {}),
|
|
424
424
|
messages: [
|
|
425
|
-
{ role: "system", content: [{ type: "text", text: STR.prompts.narrative[language] }], source: { kind: "plugin", plugin: "dsh-mneme" } },
|
|
426
|
-
{ role: "user", content: [{ type: "text", text: listing }], source: { kind: "plugin", plugin: "dsh-mneme" } }
|
|
425
|
+
{ role: "system", content: [{ type: "text", text: STR.prompts.narrative[language] }], source: { kind: "plugin:dsh-mneme", plugin: "dsh-mneme" } },
|
|
426
|
+
{ role: "user", content: [{ type: "text", text: listing }], source: { kind: "plugin:dsh-mneme", plugin: "dsh-mneme" } }
|
|
427
427
|
]
|
|
428
428
|
}, reportUsage, (reason) => { streamFailure = describeStreamFailure(reason); }));
|
|
429
429
|
};
|
|
@@ -1100,8 +1100,8 @@ export function createDreamScheduler({ onRun, thresholdCount = 10, thresholdChar
|
|
|
1100
1100
|
maxTokens: config.dreamMaxTokens ?? 4096,
|
|
1101
1101
|
...(withEffort && effort ? { reasoningEffort: effort } : {}),
|
|
1102
1102
|
messages: [
|
|
1103
|
-
{ role: "system", content: [{ type: "text", text: consolidationPrompt }], source: { kind: "plugin", plugin: "dsh-mneme" } },
|
|
1104
|
-
{ role: "user", content: [{ type: "text", text: listText }], source: { kind: "plugin", plugin: "dsh-mneme" } }
|
|
1103
|
+
{ role: "system", content: [{ type: "text", text: consolidationPrompt }], source: { kind: "plugin:dsh-mneme", plugin: "dsh-mneme" } },
|
|
1104
|
+
{ role: "user", content: [{ type: "text", text: listText }], source: { kind: "plugin:dsh-mneme", plugin: "dsh-mneme" } }
|
|
1105
1105
|
]
|
|
1106
1106
|
}, reportUsage, (reason) => { streamFailure = describeStreamFailure(reason); }));
|
|
1107
1107
|
};
|
|
@@ -1335,8 +1335,8 @@ export function createDreamScheduler({ onRun, thresholdCount = 10, thresholdChar
|
|
|
1335
1335
|
maxTokens: config.dreamMaxTokens ?? 2048,
|
|
1336
1336
|
...(withEffort && effort ? { reasoningEffort: effort } : {}),
|
|
1337
1337
|
messages: [
|
|
1338
|
-
{ role: "system", content: [{ type: "text", text: STR.prompts.dreamSummary[language] }], source: { kind: "plugin", plugin: "dsh-mneme" } },
|
|
1339
|
-
{ role: "user", content: [{ type: "text", text: summaryInputs.map((m) => `- ${m.title}: ${m.content}`).join("\n") }], source: { kind: "plugin", plugin: "dsh-mneme" } }
|
|
1338
|
+
{ role: "system", content: [{ type: "text", text: STR.prompts.dreamSummary[language] }], source: { kind: "plugin:dsh-mneme", plugin: "dsh-mneme" } },
|
|
1339
|
+
{ role: "user", content: [{ type: "text", text: summaryInputs.map((m) => `- ${m.title}: ${m.content}`).join("\n") }], source: { kind: "plugin:dsh-mneme", plugin: "dsh-mneme" } }
|
|
1340
1340
|
]
|
|
1341
1341
|
}, reportUsage, (reason) => { summaryStreamFailure = describeStreamFailure(reason); }));
|
|
1342
1342
|
};
|
|
@@ -150,8 +150,8 @@ export async function extractEntities(memory, { store, config, callLLM, logger }
|
|
|
150
150
|
const userText = buildUserMessage(memory.content);
|
|
151
151
|
|
|
152
152
|
const messages = [
|
|
153
|
-
{ role: "system", content: [{ type: "text", text: systemPrompt }], source: { kind: "plugin", plugin: "dsh-mneme" } },
|
|
154
|
-
{ role: "user", content: [{ type: "text", text: userText }], source: { kind: "plugin", plugin: "dsh-mneme" } }
|
|
153
|
+
{ role: "system", content: [{ type: "text", text: systemPrompt }], source: { kind: "plugin:dsh-mneme", plugin: "dsh-mneme" } },
|
|
154
|
+
{ role: "user", content: [{ type: "text", text: userText }], source: { kind: "plugin:dsh-mneme", plugin: "dsh-mneme" } }
|
|
155
155
|
];
|
|
156
156
|
|
|
157
157
|
// Issue #109: optional provider/model override + reasoning effort are
|
package/lib/peak-hours.js
CHANGED
|
@@ -1,117 +1,117 @@
|
|
|
1
|
-
// 高峰时段(peak hours)解析与判定——纯函数模块,零依赖。
|
|
2
|
-
//
|
|
3
|
-
// 从 summarize.js 抽出(PR #320 review):#316 之后 summarize.js 反向依赖
|
|
4
|
-
// dream.js(withEffortFallback 复用),dream 镜像错峰时再 import summarize
|
|
5
|
-
// 会成真循环。时段调度本来就不是蒸馏的私有语义,独立成模块后两侧都从这里
|
|
6
|
-
// import,循环消失。re-export 兼容留在 summarize.js,调用方零改动。
|
|
7
|
-
//
|
|
8
|
-
// Issue #239(第 4 项,错峰队列):高峰时段解析与判定。纯函数、可单测——排程
|
|
9
|
-
// 判断不绑死真实时钟,测试才能确定性地覆盖跨零点、多段、星期过滤与非法写法。
|
|
10
|
-
// spec 语法:`[<星期> ]<时段>[,<时段>...]`,星期前缀可省(省 = 每天):
|
|
11
|
-
// "09:00-18:00" 每天 09:00-18:00
|
|
12
|
-
// "mon-fri 08:00-12:00,14:00-18:00" 工作日两段(ISO 1=周一…7=周日,也认 mon..sun)
|
|
13
|
-
// "sat,sun 23:00-06:00" 周末跨零点段
|
|
14
|
-
// 时间取宿主本地时区。任一写法非法 → 整串视为未配置(返回 null):排程是省钱手段,
|
|
15
|
-
// 绝不该因为写错格式把蒸馏/巩固停掉。
|
|
16
|
-
const DAY_NAMES = { mon: 1, tue: 2, wed: 3, thu: 4, fri: 5, sat: 6, sun: 7 };
|
|
17
|
-
|
|
18
|
-
function parseDayToken(token) {
|
|
19
|
-
const days = new Set();
|
|
20
|
-
const normalize = (value) => (/^\d$/.test(value) ? Number(value) : DAY_NAMES[value] ?? null);
|
|
21
|
-
for (const piece of token.split(",")) {
|
|
22
|
-
const matched = /^([a-z]{3}|\d)(?:-([a-z]{3}|\d))?$/.exec(piece.trim().toLowerCase());
|
|
23
|
-
if (!matched) return null;
|
|
24
|
-
const from = normalize(matched[1]);
|
|
25
|
-
const to = matched[2] === undefined ? from : normalize(matched[2]);
|
|
26
|
-
if (from === null || to === null || from < 1 || from > 7 || to < 1 || to > 7) return null;
|
|
27
|
-
// 支持跨周环绕(fri-mon):从 from 起逐天推进到 to,最多绕一圈。
|
|
28
|
-
for (let day = from; ; day = (day % 7) + 1) {
|
|
29
|
-
days.add(day);
|
|
30
|
-
if (day === to) break;
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
return days.size > 0 ? [...days].sort((a, b) => a - b) : null;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/** JS 的 getDay() 是 0=周日…6=周六;这里统一成 ISO(1=周一…7=周日)。 */
|
|
37
|
-
function isoDay(date) {
|
|
38
|
-
const day = date.getDay();
|
|
39
|
-
return day === 0 ? 7 : day;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export function parsePeakSpec(spec) {
|
|
43
|
-
if (typeof spec !== "string" || spec.trim() === "") return null;
|
|
44
|
-
let rest = spec.trim();
|
|
45
|
-
let days = null;
|
|
46
|
-
// 星期前缀 = 第一个空白之前的部分,但**头段含冒号就不是前缀**(那是时段本身,
|
|
47
|
-
// 例如 "09:00-12:00, 14:00-18:00" 里的逗号空格)。前缀解析失败一律按未配置处理,
|
|
48
|
-
// 不做猜测——宁可不省,也不能误停。
|
|
49
|
-
const sep = rest.search(/\s/);
|
|
50
|
-
if (sep > 0) {
|
|
51
|
-
const head = rest.slice(0, sep);
|
|
52
|
-
if (!head.includes(":")) {
|
|
53
|
-
days = parseDayToken(head);
|
|
54
|
-
if (days === null) return null;
|
|
55
|
-
rest = rest.slice(sep).trim();
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
const windows = [];
|
|
59
|
-
for (const part of rest.split(",")) {
|
|
60
|
-
const matched = /^\s*(\d{1,2}):(\d{2})\s*-\s*(\d{1,2}):(\d{2})\s*$/.exec(part);
|
|
61
|
-
if (!matched) return null;
|
|
62
|
-
const [sh, sm, eh, em] = [Number(matched[1]), Number(matched[2]), Number(matched[3]), Number(matched[4])];
|
|
63
|
-
if (sh > 23 || eh > 23 || sm > 59 || em > 59) return null;
|
|
64
|
-
const start = sh * 60 + sm;
|
|
65
|
-
const end = eh * 60 + em;
|
|
66
|
-
if (start === end) return null;
|
|
67
|
-
windows.push({ start, end });
|
|
68
|
-
}
|
|
69
|
-
return windows.length > 0 ? { days, windows } : null;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* 该时刻是否落在高峰内。跨零点段(start > end)按「窗口所属的那一天」认星期:
|
|
74
|
-
* `mon-fri 23:00-06:00` 的周六 02:00 属于周五开的那个窗口,仍算高峰。
|
|
75
|
-
*/
|
|
76
|
-
export function isInPeakWindow(date, spec) {
|
|
77
|
-
const parsed = parsePeakSpec(spec);
|
|
78
|
-
if (!parsed) return false;
|
|
79
|
-
const minutes = date.getHours() * 60 + date.getMinutes();
|
|
80
|
-
const today = isoDay(date);
|
|
81
|
-
const yesterday = today === 1 ? 7 : today - 1;
|
|
82
|
-
const allowed = (day) => !parsed.days || parsed.days.includes(day);
|
|
83
|
-
return parsed.windows.some(({ start, end }) => {
|
|
84
|
-
if (start < end) return minutes >= start && minutes < end && allowed(today);
|
|
85
|
-
return (minutes >= start && allowed(today)) || (minutes < end && allowed(yesterday));
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* 高峰内则返回「距当前最近的一个高峰结束时刻」(择时补跑用),否则 null。
|
|
91
|
-
* 落在多个时段重叠处时取最早结束的那个——早跑不亏,晚跑才亏。
|
|
92
|
-
*/
|
|
93
|
-
export function nextOffPeakAt(date, spec) {
|
|
94
|
-
const parsed = parsePeakSpec(spec);
|
|
95
|
-
if (!parsed) return null;
|
|
96
|
-
const minutes = date.getHours() * 60 + date.getMinutes();
|
|
97
|
-
const today = isoDay(date);
|
|
98
|
-
const yesterday = today === 1 ? 7 : today - 1;
|
|
99
|
-
const allowed = (day) => !parsed.days || parsed.days.includes(day);
|
|
100
|
-
let bestDelta = null;
|
|
101
|
-
const consider = (delta) => {
|
|
102
|
-
if (bestDelta === null || delta < bestDelta) bestDelta = delta;
|
|
103
|
-
};
|
|
104
|
-
for (const { start, end } of parsed.windows) {
|
|
105
|
-
if (start < end) {
|
|
106
|
-
if (minutes >= start && minutes < end && allowed(today)) consider(end - minutes);
|
|
107
|
-
continue;
|
|
108
|
-
}
|
|
109
|
-
if (minutes >= start && allowed(today)) consider((1440 - minutes) + end);
|
|
110
|
-
else if (minutes < end && allowed(yesterday)) consider(end - minutes);
|
|
111
|
-
}
|
|
112
|
-
if (bestDelta === null) return null;
|
|
113
|
-
const at = new Date(date.getTime());
|
|
114
|
-
at.setSeconds(0, 0);
|
|
115
|
-
at.setMinutes(at.getMinutes() + bestDelta);
|
|
116
|
-
return at;
|
|
117
|
-
}
|
|
1
|
+
// 高峰时段(peak hours)解析与判定——纯函数模块,零依赖。
|
|
2
|
+
//
|
|
3
|
+
// 从 summarize.js 抽出(PR #320 review):#316 之后 summarize.js 反向依赖
|
|
4
|
+
// dream.js(withEffortFallback 复用),dream 镜像错峰时再 import summarize
|
|
5
|
+
// 会成真循环。时段调度本来就不是蒸馏的私有语义,独立成模块后两侧都从这里
|
|
6
|
+
// import,循环消失。re-export 兼容留在 summarize.js,调用方零改动。
|
|
7
|
+
//
|
|
8
|
+
// Issue #239(第 4 项,错峰队列):高峰时段解析与判定。纯函数、可单测——排程
|
|
9
|
+
// 判断不绑死真实时钟,测试才能确定性地覆盖跨零点、多段、星期过滤与非法写法。
|
|
10
|
+
// spec 语法:`[<星期> ]<时段>[,<时段>...]`,星期前缀可省(省 = 每天):
|
|
11
|
+
// "09:00-18:00" 每天 09:00-18:00
|
|
12
|
+
// "mon-fri 08:00-12:00,14:00-18:00" 工作日两段(ISO 1=周一…7=周日,也认 mon..sun)
|
|
13
|
+
// "sat,sun 23:00-06:00" 周末跨零点段
|
|
14
|
+
// 时间取宿主本地时区。任一写法非法 → 整串视为未配置(返回 null):排程是省钱手段,
|
|
15
|
+
// 绝不该因为写错格式把蒸馏/巩固停掉。
|
|
16
|
+
const DAY_NAMES = { mon: 1, tue: 2, wed: 3, thu: 4, fri: 5, sat: 6, sun: 7 };
|
|
17
|
+
|
|
18
|
+
function parseDayToken(token) {
|
|
19
|
+
const days = new Set();
|
|
20
|
+
const normalize = (value) => (/^\d$/.test(value) ? Number(value) : DAY_NAMES[value] ?? null);
|
|
21
|
+
for (const piece of token.split(",")) {
|
|
22
|
+
const matched = /^([a-z]{3}|\d)(?:-([a-z]{3}|\d))?$/.exec(piece.trim().toLowerCase());
|
|
23
|
+
if (!matched) return null;
|
|
24
|
+
const from = normalize(matched[1]);
|
|
25
|
+
const to = matched[2] === undefined ? from : normalize(matched[2]);
|
|
26
|
+
if (from === null || to === null || from < 1 || from > 7 || to < 1 || to > 7) return null;
|
|
27
|
+
// 支持跨周环绕(fri-mon):从 from 起逐天推进到 to,最多绕一圈。
|
|
28
|
+
for (let day = from; ; day = (day % 7) + 1) {
|
|
29
|
+
days.add(day);
|
|
30
|
+
if (day === to) break;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return days.size > 0 ? [...days].sort((a, b) => a - b) : null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** JS 的 getDay() 是 0=周日…6=周六;这里统一成 ISO(1=周一…7=周日)。 */
|
|
37
|
+
function isoDay(date) {
|
|
38
|
+
const day = date.getDay();
|
|
39
|
+
return day === 0 ? 7 : day;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function parsePeakSpec(spec) {
|
|
43
|
+
if (typeof spec !== "string" || spec.trim() === "") return null;
|
|
44
|
+
let rest = spec.trim();
|
|
45
|
+
let days = null;
|
|
46
|
+
// 星期前缀 = 第一个空白之前的部分,但**头段含冒号就不是前缀**(那是时段本身,
|
|
47
|
+
// 例如 "09:00-12:00, 14:00-18:00" 里的逗号空格)。前缀解析失败一律按未配置处理,
|
|
48
|
+
// 不做猜测——宁可不省,也不能误停。
|
|
49
|
+
const sep = rest.search(/\s/);
|
|
50
|
+
if (sep > 0) {
|
|
51
|
+
const head = rest.slice(0, sep);
|
|
52
|
+
if (!head.includes(":")) {
|
|
53
|
+
days = parseDayToken(head);
|
|
54
|
+
if (days === null) return null;
|
|
55
|
+
rest = rest.slice(sep).trim();
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
const windows = [];
|
|
59
|
+
for (const part of rest.split(",")) {
|
|
60
|
+
const matched = /^\s*(\d{1,2}):(\d{2})\s*-\s*(\d{1,2}):(\d{2})\s*$/.exec(part);
|
|
61
|
+
if (!matched) return null;
|
|
62
|
+
const [sh, sm, eh, em] = [Number(matched[1]), Number(matched[2]), Number(matched[3]), Number(matched[4])];
|
|
63
|
+
if (sh > 23 || eh > 23 || sm > 59 || em > 59) return null;
|
|
64
|
+
const start = sh * 60 + sm;
|
|
65
|
+
const end = eh * 60 + em;
|
|
66
|
+
if (start === end) return null;
|
|
67
|
+
windows.push({ start, end });
|
|
68
|
+
}
|
|
69
|
+
return windows.length > 0 ? { days, windows } : null;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* 该时刻是否落在高峰内。跨零点段(start > end)按「窗口所属的那一天」认星期:
|
|
74
|
+
* `mon-fri 23:00-06:00` 的周六 02:00 属于周五开的那个窗口,仍算高峰。
|
|
75
|
+
*/
|
|
76
|
+
export function isInPeakWindow(date, spec) {
|
|
77
|
+
const parsed = parsePeakSpec(spec);
|
|
78
|
+
if (!parsed) return false;
|
|
79
|
+
const minutes = date.getHours() * 60 + date.getMinutes();
|
|
80
|
+
const today = isoDay(date);
|
|
81
|
+
const yesterday = today === 1 ? 7 : today - 1;
|
|
82
|
+
const allowed = (day) => !parsed.days || parsed.days.includes(day);
|
|
83
|
+
return parsed.windows.some(({ start, end }) => {
|
|
84
|
+
if (start < end) return minutes >= start && minutes < end && allowed(today);
|
|
85
|
+
return (minutes >= start && allowed(today)) || (minutes < end && allowed(yesterday));
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* 高峰内则返回「距当前最近的一个高峰结束时刻」(择时补跑用),否则 null。
|
|
91
|
+
* 落在多个时段重叠处时取最早结束的那个——早跑不亏,晚跑才亏。
|
|
92
|
+
*/
|
|
93
|
+
export function nextOffPeakAt(date, spec) {
|
|
94
|
+
const parsed = parsePeakSpec(spec);
|
|
95
|
+
if (!parsed) return null;
|
|
96
|
+
const minutes = date.getHours() * 60 + date.getMinutes();
|
|
97
|
+
const today = isoDay(date);
|
|
98
|
+
const yesterday = today === 1 ? 7 : today - 1;
|
|
99
|
+
const allowed = (day) => !parsed.days || parsed.days.includes(day);
|
|
100
|
+
let bestDelta = null;
|
|
101
|
+
const consider = (delta) => {
|
|
102
|
+
if (bestDelta === null || delta < bestDelta) bestDelta = delta;
|
|
103
|
+
};
|
|
104
|
+
for (const { start, end } of parsed.windows) {
|
|
105
|
+
if (start < end) {
|
|
106
|
+
if (minutes >= start && minutes < end && allowed(today)) consider(end - minutes);
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
if (minutes >= start && allowed(today)) consider((1440 - minutes) + end);
|
|
110
|
+
else if (minutes < end && allowed(yesterday)) consider(end - minutes);
|
|
111
|
+
}
|
|
112
|
+
if (bestDelta === null) return null;
|
|
113
|
+
const at = new Date(date.getTime());
|
|
114
|
+
at.setSeconds(0, 0);
|
|
115
|
+
at.setMinutes(at.getMinutes() + bestDelta);
|
|
116
|
+
return at;
|
|
117
|
+
}
|
package/lib/summarize.js
CHANGED
|
@@ -220,7 +220,7 @@ function collectMessages(session, maxChars = 8000, language = "zh", afterSeq, th
|
|
|
220
220
|
}, undefined);
|
|
221
221
|
return {
|
|
222
222
|
messages: lines.length
|
|
223
|
-
? [createUserMessage({ content: [{ type: "text", text: trimTranscript(lines.join("\n"), maxChars) }], source: { kind: "plugin", plugin: "dsh-mneme" } })]
|
|
223
|
+
? [createUserMessage({ content: [{ type: "text", text: trimTranscript(lines.join("\n"), maxChars) }], source: { kind: "plugin:dsh-mneme", plugin: "dsh-mneme" } })]
|
|
224
224
|
: [],
|
|
225
225
|
hasEvents: events.length > 0,
|
|
226
226
|
lastSeq
|
|
@@ -495,7 +495,7 @@ export function createSummarizer(ctx, service, config, deps = {}) {
|
|
|
495
495
|
model: route.model,
|
|
496
496
|
purpose: "summarization",
|
|
497
497
|
messages: [
|
|
498
|
-
{ role: "system", content: [{ type: "text", text: config.codingRetrospect ? STR.prompts.codingSummary[langOf(config)] : STR.prompts.summary[langOf(config)] }], source: { kind: "plugin", plugin: "dsh-mneme" } },
|
|
498
|
+
{ role: "system", content: [{ type: "text", text: config.codingRetrospect ? STR.prompts.codingSummary[langOf(config)] : STR.prompts.summary[langOf(config)] }], source: { kind: "plugin:dsh-mneme", plugin: "dsh-mneme" } },
|
|
499
499
|
...messages
|
|
500
500
|
],
|
|
501
501
|
signal: controller.signal
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@modusensus/dsh-mneme",
|
|
3
3
|
"description": "Cross-session memory plugin for DeepSeek Harness with autoDream consolidation: SQLite store, Markdown mirrors, 9 model tools, automatic injection, session summarization, user profile/rules, custom slash commands, vector (semantic) search, and a Web GUI panel",
|
|
4
|
-
"version": "0.8.
|
|
4
|
+
"version": "0.8.9",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
package/src/api.js
CHANGED
|
@@ -380,7 +380,7 @@ export function createApi(ctx, service, settings, commands, embedder, semantic =
|
|
|
380
380
|
{
|
|
381
381
|
role: "user",
|
|
382
382
|
content: [{ type: "text", text: "Reply with exactly one word: ok" }],
|
|
383
|
-
source: { kind: "plugin", plugin: "dsh-mneme" }
|
|
383
|
+
source: { kind: "plugin:dsh-mneme", plugin: "dsh-mneme" }
|
|
384
384
|
}
|
|
385
385
|
]
|
|
386
386
|
})) {
|
package/src/continuity.js
CHANGED
|
@@ -205,7 +205,7 @@ export function createContinuityRescue(ctx, store) {
|
|
|
205
205
|
...decision,
|
|
206
206
|
messages: [
|
|
207
207
|
...messages,
|
|
208
|
-
createUserMessage({ content: [{ type: "text", text }], source: { kind: "plugin", plugin: "dsh-mneme" } })
|
|
208
|
+
createUserMessage({ content: [{ type: "text", text }], source: { kind: "plugin:dsh-mneme", plugin: "dsh-mneme" } })
|
|
209
209
|
]
|
|
210
210
|
};
|
|
211
211
|
});
|
package/src/dream/sleep.js
CHANGED
|
@@ -334,8 +334,8 @@ async function phaseConflicts(ctx, service, config, logger, runId, semantic = nu
|
|
|
334
334
|
maxTokens: config.sleepMaxTokens ?? 2048,
|
|
335
335
|
...(withEffort && sleepEffort ? { reasoningEffort: sleepEffort } : {}),
|
|
336
336
|
messages: [
|
|
337
|
-
{ role: "system", content: [{ type: "text", text: conflictPrompt }], source: { kind: "plugin", plugin: "dsh-mneme" } },
|
|
338
|
-
{ role: "user", content: [{ type: "text", text: listText }], source: { kind: "plugin", plugin: "dsh-mneme" } }
|
|
337
|
+
{ role: "system", content: [{ type: "text", text: conflictPrompt }], source: { kind: "plugin:dsh-mneme", plugin: "dsh-mneme" } },
|
|
338
|
+
{ role: "user", content: [{ type: "text", text: listText }], source: { kind: "plugin:dsh-mneme", plugin: "dsh-mneme" } }
|
|
339
339
|
]
|
|
340
340
|
}, reportUsage, (reason) => { conflictStreamFailure = describeStreamFailure(reason); }));
|
|
341
341
|
};
|
|
@@ -496,8 +496,8 @@ async function phasePatterns(ctx, service, config, logger, runId, signal = null)
|
|
|
496
496
|
maxTokens: config.sleepMaxTokens ?? 2048,
|
|
497
497
|
...(withEffort && sleepEffort ? { reasoningEffort: sleepEffort } : {}),
|
|
498
498
|
messages: [
|
|
499
|
-
{ role: "system", content: [{ type: "text", text: STR.prompts.pattern[language].replace("N", String(maxPatterns)) }], source: { kind: "plugin", plugin: "dsh-mneme" } },
|
|
500
|
-
{ role: "user", content: [{ type: "text", text: listText }], source: { kind: "plugin", plugin: "dsh-mneme" } }
|
|
499
|
+
{ role: "system", content: [{ type: "text", text: STR.prompts.pattern[language].replace("N", String(maxPatterns)) }], source: { kind: "plugin:dsh-mneme", plugin: "dsh-mneme" } },
|
|
500
|
+
{ role: "user", content: [{ type: "text", text: listText }], source: { kind: "plugin:dsh-mneme", plugin: "dsh-mneme" } }
|
|
501
501
|
]
|
|
502
502
|
}, reportUsage, (reason) => { patternStreamFailure = describeStreamFailure(reason); }));
|
|
503
503
|
};
|
package/src/dream.js
CHANGED
|
@@ -422,8 +422,8 @@ async function generateNarratives({ ctx, service, config, route, language, effor
|
|
|
422
422
|
maxTokens: config.dreamMaxTokens ?? 2048,
|
|
423
423
|
...(withEffort && effort ? { reasoningEffort: effort } : {}),
|
|
424
424
|
messages: [
|
|
425
|
-
{ role: "system", content: [{ type: "text", text: STR.prompts.narrative[language] }], source: { kind: "plugin", plugin: "dsh-mneme" } },
|
|
426
|
-
{ role: "user", content: [{ type: "text", text: listing }], source: { kind: "plugin", plugin: "dsh-mneme" } }
|
|
425
|
+
{ role: "system", content: [{ type: "text", text: STR.prompts.narrative[language] }], source: { kind: "plugin:dsh-mneme", plugin: "dsh-mneme" } },
|
|
426
|
+
{ role: "user", content: [{ type: "text", text: listing }], source: { kind: "plugin:dsh-mneme", plugin: "dsh-mneme" } }
|
|
427
427
|
]
|
|
428
428
|
}, reportUsage, (reason) => { streamFailure = describeStreamFailure(reason); }));
|
|
429
429
|
};
|
|
@@ -1100,8 +1100,8 @@ export function createDreamScheduler({ onRun, thresholdCount = 10, thresholdChar
|
|
|
1100
1100
|
maxTokens: config.dreamMaxTokens ?? 4096,
|
|
1101
1101
|
...(withEffort && effort ? { reasoningEffort: effort } : {}),
|
|
1102
1102
|
messages: [
|
|
1103
|
-
{ role: "system", content: [{ type: "text", text: consolidationPrompt }], source: { kind: "plugin", plugin: "dsh-mneme" } },
|
|
1104
|
-
{ role: "user", content: [{ type: "text", text: listText }], source: { kind: "plugin", plugin: "dsh-mneme" } }
|
|
1103
|
+
{ role: "system", content: [{ type: "text", text: consolidationPrompt }], source: { kind: "plugin:dsh-mneme", plugin: "dsh-mneme" } },
|
|
1104
|
+
{ role: "user", content: [{ type: "text", text: listText }], source: { kind: "plugin:dsh-mneme", plugin: "dsh-mneme" } }
|
|
1105
1105
|
]
|
|
1106
1106
|
}, reportUsage, (reason) => { streamFailure = describeStreamFailure(reason); }));
|
|
1107
1107
|
};
|
|
@@ -1335,8 +1335,8 @@ export function createDreamScheduler({ onRun, thresholdCount = 10, thresholdChar
|
|
|
1335
1335
|
maxTokens: config.dreamMaxTokens ?? 2048,
|
|
1336
1336
|
...(withEffort && effort ? { reasoningEffort: effort } : {}),
|
|
1337
1337
|
messages: [
|
|
1338
|
-
{ role: "system", content: [{ type: "text", text: STR.prompts.dreamSummary[language] }], source: { kind: "plugin", plugin: "dsh-mneme" } },
|
|
1339
|
-
{ role: "user", content: [{ type: "text", text: summaryInputs.map((m) => `- ${m.title}: ${m.content}`).join("\n") }], source: { kind: "plugin", plugin: "dsh-mneme" } }
|
|
1338
|
+
{ role: "system", content: [{ type: "text", text: STR.prompts.dreamSummary[language] }], source: { kind: "plugin:dsh-mneme", plugin: "dsh-mneme" } },
|
|
1339
|
+
{ role: "user", content: [{ type: "text", text: summaryInputs.map((m) => `- ${m.title}: ${m.content}`).join("\n") }], source: { kind: "plugin:dsh-mneme", plugin: "dsh-mneme" } }
|
|
1340
1340
|
]
|
|
1341
1341
|
}, reportUsage, (reason) => { summaryStreamFailure = describeStreamFailure(reason); }));
|
|
1342
1342
|
};
|
|
@@ -150,8 +150,8 @@ export async function extractEntities(memory, { store, config, callLLM, logger }
|
|
|
150
150
|
const userText = buildUserMessage(memory.content);
|
|
151
151
|
|
|
152
152
|
const messages = [
|
|
153
|
-
{ role: "system", content: [{ type: "text", text: systemPrompt }], source: { kind: "plugin", plugin: "dsh-mneme" } },
|
|
154
|
-
{ role: "user", content: [{ type: "text", text: userText }], source: { kind: "plugin", plugin: "dsh-mneme" } }
|
|
153
|
+
{ role: "system", content: [{ type: "text", text: systemPrompt }], source: { kind: "plugin:dsh-mneme", plugin: "dsh-mneme" } },
|
|
154
|
+
{ role: "user", content: [{ type: "text", text: userText }], source: { kind: "plugin:dsh-mneme", plugin: "dsh-mneme" } }
|
|
155
155
|
];
|
|
156
156
|
|
|
157
157
|
// Issue #109: optional provider/model override + reasoning effort are
|