dsh-galatea 0.3.0 → 0.3.5
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/index.js +1 -1
- package/lib/beat-tool.js +9 -1
- package/lib/journal.js +86 -0
- package/lib/material.js +1 -1
- package/package.json +7 -2
package/index.js
CHANGED
package/lib/beat-tool.js
CHANGED
|
@@ -12,6 +12,7 @@ import { defineTool } from '@deepseek-ai/dsh-tools';
|
|
|
12
12
|
|
|
13
13
|
import { planBeat } from './beat.js';
|
|
14
14
|
import { pickMaterial } from './material.js';
|
|
15
|
+
import { renderJournal } from './journal.js';
|
|
15
16
|
|
|
16
17
|
/**
|
|
17
18
|
* 把 agents 服务里的 live agent 统一成 { id, header }。
|
|
@@ -58,6 +59,7 @@ export function makeBeatTool(deps) {
|
|
|
58
59
|
switched: { type: 'boolean' },
|
|
59
60
|
previous: { type: 'string' },
|
|
60
61
|
text: { type: 'string' },
|
|
62
|
+
journal: { type: 'string' },
|
|
61
63
|
},
|
|
62
64
|
},
|
|
63
65
|
render: (_args, v) => [{ type: 'text', text: v.text }],
|
|
@@ -68,6 +70,8 @@ export function makeBeatTool(deps) {
|
|
|
68
70
|
const entries = deps.listEntries();
|
|
69
71
|
const state = deps.loadState();
|
|
70
72
|
|
|
73
|
+
const liveCfg = typeof deps.config === 'function' ? deps.config() : (deps.config ?? {});
|
|
74
|
+
|
|
71
75
|
const r = planBeat({
|
|
72
76
|
now: new Date(),
|
|
73
77
|
state,
|
|
@@ -75,12 +79,15 @@ export function makeBeatTool(deps) {
|
|
|
75
79
|
entries,
|
|
76
80
|
// 配置可以是函数:设置页改的是引用,每次现读才拿得到新值。
|
|
77
81
|
// 传快照的话,界面上改了、跑起来还是旧的,而且不报错。
|
|
78
|
-
config:
|
|
82
|
+
config: liveCfg,
|
|
79
83
|
});
|
|
80
84
|
|
|
81
85
|
const material = entries.find((e) => e.id === r.materialId) ?? null;
|
|
82
86
|
const picked = material ?? pickMaterial(entries, state.lastSpokeAt ?? null).entry;
|
|
83
87
|
|
|
88
|
+
// 给人看的那几行:把它自己最近的决定翻成人话。
|
|
89
|
+
const journal = renderJournal(state, { quietRounds: liveCfg.quietRounds });
|
|
90
|
+
|
|
84
91
|
const lines = [];
|
|
85
92
|
lines.push(r.speak ? '现在会说话。' : '现在不会说话。');
|
|
86
93
|
lines.push(`理由:${r.reason} —— ${r.detail}`);
|
|
@@ -100,6 +107,7 @@ export function makeBeatTool(deps) {
|
|
|
100
107
|
switched: r.switched === true,
|
|
101
108
|
previous: r.previous ?? '',
|
|
102
109
|
text: lines.join('\n'),
|
|
110
|
+
journal,
|
|
103
111
|
};
|
|
104
112
|
},
|
|
105
113
|
});
|
package/lib/journal.js
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 给"人"看的心跳记录。
|
|
3
|
+
*
|
|
4
|
+
* 来由:外面有人真机试完,说了一句最狠的话 ——
|
|
5
|
+
* **「Galatea 的能力都在用户看不见的地方。」**
|
|
6
|
+
*
|
|
7
|
+
* 门控、状态账本、素材选择全在后台跑;装完打开,用户看见的是一片空白。
|
|
8
|
+
* 更糟的是:**沉默看不见,就等于故障看不见。**
|
|
9
|
+
* 我在代码里写了七种「为什么没说」,但那些字只有读代码的人才看得到。
|
|
10
|
+
*
|
|
11
|
+
* 所以这里只做一件事:把那七种理由翻成人话。
|
|
12
|
+
* 不发明数据,不评价好坏,不排序打分——账本里记了什么就说什么。
|
|
13
|
+
*/
|
|
14
|
+
import { DEFAULTS } from './heartbeat.js';
|
|
15
|
+
|
|
16
|
+
/** reason → 一句人话。门控那七种 + 三种"更早就断了"的,一种都不许漏。 */
|
|
17
|
+
export const REASON_TEXT = {
|
|
18
|
+
// 门控给出的
|
|
19
|
+
quiet: '安静时段',
|
|
20
|
+
tooSoon: '离上一轮太近',
|
|
21
|
+
cooldown: '你刚说过话',
|
|
22
|
+
hard: '有具体的事要说',
|
|
23
|
+
floor: '太久没开口,这次强制说一句',
|
|
24
|
+
dice: '随机决定要说',
|
|
25
|
+
noMaterial: '记忆里没有可用的东西',
|
|
26
|
+
nothingNew: '记下的都说过了',
|
|
27
|
+
noHuman: '没有正在进行的对话',
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/** HH:MM,本地时区。认不出的时间原样标出来,不替它猜一个像样的值。 */
|
|
31
|
+
function clockOf(iso) {
|
|
32
|
+
const d = new Date(iso);
|
|
33
|
+
if (Number.isNaN(d.getTime())) return '--:--';
|
|
34
|
+
const p = (n) => String(n).padStart(2, '0');
|
|
35
|
+
return `${p(d.getHours())}:${p(d.getMinutes())}`;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** 拿不准的一律原样显示,不替它编一个更好听的说法。 */
|
|
39
|
+
export function explain(reason) {
|
|
40
|
+
return REASON_TEXT[reason] ?? `(未登记的理由:${reason})`;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* 把状态账本渲染成人能读的几行。
|
|
45
|
+
*
|
|
46
|
+
* 只读 `decisions`,不推测、不补空白。
|
|
47
|
+
* 一条记录都没有时,明说「还没有记录过」——不能显示空白,
|
|
48
|
+
* 那和「功能坏了」长得一样。这条规矩从记忆模块一路沿用到这里。
|
|
49
|
+
*
|
|
50
|
+
* @param {object} state loadState() 的输出
|
|
51
|
+
* @param {{limit?: number}} [opts]
|
|
52
|
+
* @returns {string}
|
|
53
|
+
*/
|
|
54
|
+
export function renderJournal(state, opts = {}) {
|
|
55
|
+
const limit = opts.limit ?? 5;
|
|
56
|
+
const ds = Array.isArray(state?.decisions) ? state.decisions : [];
|
|
57
|
+
|
|
58
|
+
const lines = [];
|
|
59
|
+
|
|
60
|
+
if (ds.length === 0) {
|
|
61
|
+
// 不能直接 return —— 没有记录和"连续很多轮没开口"是两件可以同时成立的事,
|
|
62
|
+
// 早退会把 streak 吞掉,而那正是沉默和坏了唯一的分界。
|
|
63
|
+
lines.push('心跳还没有记录过。它至少跑过一次之后,这里才会有东西。');
|
|
64
|
+
} else {
|
|
65
|
+
lines.push(
|
|
66
|
+
...ds
|
|
67
|
+
.slice(-limit)
|
|
68
|
+
.reverse()
|
|
69
|
+
.map((d) => `${clockOf(d.at)} ${d.speak ? '说了' : '没说'} — ${explain(d.reason)}`),
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// 连续没开口的次数:这是「沉默」和「坏了」唯一能分开的地方
|
|
74
|
+
const streak = Number(state?.quietStreak ?? 0);
|
|
75
|
+
if (streak > 0) {
|
|
76
|
+
const floor = Number(opts.quietRounds ?? DEFAULTS.quietRounds);
|
|
77
|
+
if (lines.length > 0) lines.push('');
|
|
78
|
+
lines.push(
|
|
79
|
+
floor > 0
|
|
80
|
+
? `连续 ${streak} 轮没开口(到 ${floor} 会强制说一次)`
|
|
81
|
+
: `连续 ${streak} 轮没开口(保底已关闭,可能一直不说)`,
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return lines.join('\n');
|
|
86
|
+
}
|
package/lib/material.js
CHANGED
|
@@ -45,7 +45,7 @@ export function pickMaterial(entries, lastSpokeAt) {
|
|
|
45
45
|
|
|
46
46
|
// 时间认不出的条目不用 —— 宁可少说一条,不要拿一条没时间的去凑
|
|
47
47
|
const dated = list
|
|
48
|
-
.map((e) => ({ entry: e, ms: toMs(e.at) }))
|
|
48
|
+
.map((e) => ({ entry: e, ms: toMs(e.at ?? e.date) }))
|
|
49
49
|
.filter((x) => x.ms !== null);
|
|
50
50
|
|
|
51
51
|
if (dated.length === 0) {
|
package/package.json
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-galatea",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.5",
|
|
4
4
|
"description": "Galatea — memory, heartbeat and continuity for DeepSeek Harness agents.",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "git+https://github.com/Vounea/Galatea.git",
|
|
8
|
+
"directory": "."
|
|
9
|
+
},
|
|
5
10
|
"type": "module",
|
|
6
11
|
"main": "index.js",
|
|
7
12
|
"license": "MIT",
|
|
@@ -16,7 +21,7 @@
|
|
|
16
21
|
"node": ">=20.18"
|
|
17
22
|
},
|
|
18
23
|
"scripts": {
|
|
19
|
-
"test": "node --test test/store.test.mjs test/apply.test.mjs test/tools.test.mjs test/heartbeat.test.mjs test/heartbeat-runner.test.mjs test/target.test.mjs test/material.test.mjs test/beat.test.mjs"
|
|
24
|
+
"test": "node --test test/store.test.mjs test/apply.test.mjs test/tools.test.mjs test/heartbeat.test.mjs test/heartbeat-runner.test.mjs test/target.test.mjs test/material.test.mjs test/beat.test.mjs test/contract.test.mjs test/journal.test.mjs"
|
|
20
25
|
},
|
|
21
26
|
"peerDependencies": {
|
|
22
27
|
"@deepseek-ai/dsh-tools": "*",
|