@kanadego/dsh-heartbeat 1.9.2 → 2.0.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 (57) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +344 -396
  3. package/client.js +108 -20
  4. package/config/policy.json +74 -73
  5. package/config/profile-schema.json +306 -306
  6. package/dist/{bindings-225SI7KC.js → bindings-U2M3CO2D.js} +3 -3
  7. package/dist/{chunk-TY6UP74X.js → chunk-3DNGY4DS.js} +2 -2
  8. package/dist/{chunk-CUOSICYJ.js → chunk-544QOJN5.js} +397 -23
  9. package/dist/chunk-544QOJN5.js.map +1 -0
  10. package/dist/{chunk-AJ5HLAL7.js → chunk-6MVKWICX.js} +708 -154
  11. package/dist/chunk-6MVKWICX.js.map +1 -0
  12. package/dist/{chunk-IG6YMUHW.js → chunk-6XM6KY7M.js} +43 -12
  13. package/dist/chunk-6XM6KY7M.js.map +1 -0
  14. package/dist/chunk-AKIJ4LS2.js +680 -0
  15. package/dist/chunk-AKIJ4LS2.js.map +1 -0
  16. package/dist/{chunk-KB5SMG3F.js → chunk-DC5HMCJC.js} +3 -3
  17. package/dist/{chunk-3MDBU6WY.js → chunk-DZ7YCFGX.js} +37 -7
  18. package/dist/chunk-DZ7YCFGX.js.map +1 -0
  19. package/dist/{chunk-WRUTATW4.js → chunk-IBBAISSW.js} +13 -2
  20. package/dist/chunk-IBBAISSW.js.map +1 -0
  21. package/dist/{chunk-ZLV4NMFX.js → chunk-IFRMKVIC.js} +85 -21
  22. package/dist/chunk-IFRMKVIC.js.map +1 -0
  23. package/dist/{chunk-XFNCTOW7.js → chunk-NCPKOWS7.js} +52 -4
  24. package/dist/chunk-NCPKOWS7.js.map +1 -0
  25. package/dist/{chunk-JDH2LDFS.js → chunk-TVX4HV4Y.js} +4 -6
  26. package/dist/{chunk-JDH2LDFS.js.map → chunk-TVX4HV4Y.js.map} +1 -1
  27. package/dist/cli/index.js +149 -26
  28. package/dist/cli/index.js.map +1 -1
  29. package/dist/{inbox-P26DDMWC.js → inbox-OWBK6PE2.js} +8 -7
  30. package/dist/index.d.ts +50 -315
  31. package/dist/index.js +288 -920
  32. package/dist/index.js.map +1 -1
  33. package/dist/migrate-TQ5PB2DI.js +27 -0
  34. package/dist/{orchestrator-EUTZ3KG6.js → orchestrator-P7AEWVYR.js} +27 -12
  35. package/dist/{pool-UTXQJJ5C.js → pool-2T6TVS2L.js} +4 -5
  36. package/dist/{snapshot-K5VHETTK.js → snapshot-F5B2DD56.js} +5 -5
  37. package/package.json +84 -83
  38. package/dist/chunk-3MDBU6WY.js.map +0 -1
  39. package/dist/chunk-AJ5HLAL7.js.map +0 -1
  40. package/dist/chunk-CUOSICYJ.js.map +0 -1
  41. package/dist/chunk-IG6YMUHW.js.map +0 -1
  42. package/dist/chunk-IV2ZWQA3.js +0 -56
  43. package/dist/chunk-IV2ZWQA3.js.map +0 -1
  44. package/dist/chunk-K5Y6JP2B.js +0 -399
  45. package/dist/chunk-K5Y6JP2B.js.map +0 -1
  46. package/dist/chunk-WRUTATW4.js.map +0 -1
  47. package/dist/chunk-XFNCTOW7.js.map +0 -1
  48. package/dist/chunk-ZLV4NMFX.js.map +0 -1
  49. package/dist/migrate-D4VKAKYT.js +0 -21
  50. /package/dist/{bindings-225SI7KC.js.map → bindings-U2M3CO2D.js.map} +0 -0
  51. /package/dist/{chunk-TY6UP74X.js.map → chunk-3DNGY4DS.js.map} +0 -0
  52. /package/dist/{chunk-KB5SMG3F.js.map → chunk-DC5HMCJC.js.map} +0 -0
  53. /package/dist/{inbox-P26DDMWC.js.map → inbox-OWBK6PE2.js.map} +0 -0
  54. /package/dist/{migrate-D4VKAKYT.js.map → migrate-TQ5PB2DI.js.map} +0 -0
  55. /package/dist/{orchestrator-EUTZ3KG6.js.map → orchestrator-P7AEWVYR.js.map} +0 -0
  56. /package/dist/{pool-UTXQJJ5C.js.map → pool-2T6TVS2L.js.map} +0 -0
  57. /package/dist/{snapshot-K5VHETTK.js.map → snapshot-F5B2DD56.js.map} +0 -0
package/client.js CHANGED
@@ -199,11 +199,12 @@ window.__ModuleLoader__.load({
199
199
  );
200
200
  }
201
201
 
202
- // ── 素材池 ────────────────────────────────────────────────────
202
+ // ── 素材池(v1.9.3 三版):话题 / 聊天 × 活跃 / 归档,四个按钮各看一类 ──
203
+ // 和画像分区同一套 tab 交互,风格统一;一次只看一类。
203
204
  function SeedsSection() {
204
205
  const [data, setData] = React.useState(null);
205
206
  const [error, setError] = React.useState(null);
206
- const [showArchived, setShowArchived] = React.useState(false);
207
+ const [tab, setTab] = React.useState("topic");
207
208
  const [confirmId, setConfirmId] = React.useState(null);
208
209
  const reload = React.useCallback(() => {
209
210
  rpc("seeds.list").then(setData, (e) => setError(String(e).slice(0, 100)));
@@ -213,8 +214,8 @@ window.__ModuleLoader__.load({
213
214
  if (!data) return React.createElement("div", { style: hintStyle }, "加载中…");
214
215
  const act = (endpoint, id) => rpc(endpoint, { id }).then(reload, (e) => setError(String(e).slice(0, 100)));
215
216
  const row = (s, archived) => React.createElement("div", { key: s.id, style: rowList },
216
- React.createElement("span", { style: { flex: 1, fontSize: 12 } },
217
- "[", s.tag, "/", s.source, "] ", s.text.slice(0, 42), " used:", s.used),
217
+ React.createElement("span", { style: { flex: 1, fontSize: 12, wordBreak: "break-word" } },
218
+ "[", s.tag, "/", s.source, "] ", s.text.slice(0, 60), " used:", s.used),
218
219
  archived
219
220
  ? React.createElement("button", { style: buttonGhost, onClick: () => act("seeds.restore", s.id) }, "恢复")
220
221
  : React.createElement("button", { style: buttonGhost, onClick: () => act("seeds.archive", s.id) }, "归档"),
@@ -222,15 +223,34 @@ window.__ModuleLoader__.load({
222
223
  ? React.createElement("button", { style: { ...buttonStyle, background: "#e5484d" }, onClick: () => { setConfirmId(null); act("seeds.delete", s.id); } }, "确认删除")
223
224
  : React.createElement("button", { style: buttonGhost, onClick: () => setConfirmId(s.id) }, "删除"),
224
225
  );
226
+ const isChat = (s) => (s.category || "topic") === "chat";
227
+ const topics = data.active.filter((s) => !isChat(s));
228
+ const chats = data.active.filter(isChat);
229
+ const arcTopics = data.archived.filter((s) => !isChat(s));
230
+ const arcChats = data.archived.filter(isChat);
231
+ const archived = tab.indexOf("arc") === 0;
232
+ const chat = tab.indexOf("chat") >= 0;
233
+ const list = archived ? (chat ? arcChats : arcTopics) : (chat ? chats : topics);
234
+ const tabButton = (key, label, count) => React.createElement("button", {
235
+ key,
236
+ style: tab === key ? buttonStyle : buttonGhost,
237
+ onClick: () => setTab(key),
238
+ }, label + "(" + count + ")");
225
239
  return React.createElement(
226
240
  "div",
227
241
  null,
228
- React.createElement("div", { style: hintStyle }, "活跃 ", data.active.length, " / ", data.cap, " 条"),
229
- data.active.map((s) => row(s, false)),
230
- data.active.length === 0 ? React.createElement("div", { style: hintStyle }, "(池子是空的——浏览流和画像会慢慢喂养它)") : null,
231
- React.createElement("div", { style: rowStyle },
232
- React.createElement("button", { style: buttonGhost, onClick: () => setShowArchived(!showArchived) }, showArchived ? "收起归档区" : "归档区(" + data.archived.length + ")")),
233
- showArchived ? data.archived.map((s) => row(s, true)) : null,
242
+ React.createElement("div", { style: hintStyle }, "活跃 ", data.active.length, " / ", data.cap, " 条;归档 ", data.archived.length, " 条"),
243
+ React.createElement("div", { style: tabRow },
244
+ tabButton("topic", "话题素材", topics.length),
245
+ tabButton("chat", "聊天素材", chats.length),
246
+ tabButton("arc-topic", "话题·归档", arcTopics.length),
247
+ tabButton("arc-chat", "聊天·归档", arcChats.length)),
248
+ React.createElement("div", { style: hintStyle }, archived
249
+ ? (chat ? "归档的聊天素材:不再被挑中,可以恢复" : "归档的话题素材:不再被挑中,可以恢复")
250
+ : (chat ? "对话里长出来的:说过一次就算用掉" : "浏览流、手写、画像喂进来的:随机挑,可重复用")),
251
+ list.length === 0
252
+ ? React.createElement("div", { style: hintStyle }, archived ? "(这类还没有归档条目)" : "(这类还没有条目)")
253
+ : React.createElement("div", { style: scrollBox }, list.map((s) => row(s, archived))),
234
254
  );
235
255
  }
236
256
 
@@ -303,29 +323,85 @@ window.__ModuleLoader__.load({
303
323
  );
304
324
  }
305
325
 
306
- // ── 画像入口 ──────────────────────────────────────────────────
326
+ // ── 画像入口(v2.0):四个分区 + 引擎室输入 ─────────────────────
327
+ // 一次只看一个分区,条目一条一块、正文自动换行;另给一个只读的
328
+ // 「引擎室输入」视图,直接回答「截断后引擎室到底看到了什么」。
329
+ const PARTITIONS = [
330
+ { key: "interest", label: "兴趣" },
331
+ { key: "projects", label: "进行中" },
332
+ { key: "comm", label: "沟通偏好" },
333
+ { key: "psy", label: "心理" },
334
+ ];
335
+ const entryBox = { borderLeft: "2px solid var(--dsw-alias-border-l2)", paddingLeft: 8, margin: "6px 0" };
336
+ // v1.9.3:分区条目一多就顶到屏幕外,给个固定高度的滚动区。
337
+ const scrollBox = { maxHeight: 320, overflowY: "auto", padding: "4px 6px", border: "1px solid var(--dsw-alias-border-l2)", borderRadius: 6 };
338
+ const groupHead = { fontSize: 12.5, fontWeight: 600, margin: "10px 0 2px" };
339
+ const entryHead = { fontSize: 12, color: "var(--dsw-alias-label-tertiary)", marginBottom: 2, wordBreak: "break-word" };
340
+ const entryBody = { fontSize: 12.5, lineHeight: 1.65, whiteSpace: "pre-wrap", wordBreak: "break-word", color: "var(--dsw-alias-label-secondary)", margin: "2px 0" };
341
+ const tabRow = { display: "flex", flexWrap: "wrap", gap: 6, margin: "6px 0" };
307
342
  function ProfileSection() {
308
343
  const [data, setData] = React.useState(null);
344
+ const [digest, setDigest] = React.useState(null);
345
+ const [tab, setTab] = React.useState("interest");
309
346
  const [error, setError] = React.useState(null);
347
+ const [status, setStatus] = React.useState("");
310
348
  const load = React.useCallback(() => {
311
- rpc("profile.digest").then(setData, (e) => setError(String(e).slice(0, 100)));
349
+ rpc("profile.partitions").then(setData, (e) => setError(String(e).slice(0, 120)));
350
+ rpc("profile.digest").then(setDigest, () => { /* 引擎室输入是附加视图,失败不挡分区 */ });
312
351
  }, []);
313
352
  React.useEffect(() => { load(); }, [load]);
314
353
  const doExport = () => rpc("profile.export").then(
315
- (v) => setData((d) => ({ ...d, exported: v.path })),
316
- (e) => setError(String(e).slice(0, 100)),
354
+ (v) => setStatus("已导出:" + v.path),
355
+ (e) => setError(String(e).slice(0, 120)),
317
356
  );
318
357
  if (error) return React.createElement("div", { style: hintStyle }, "加载失败:" + error);
319
358
  if (!data) return React.createElement("div", { style: hintStyle }, "加载中…");
359
+ const groups = data.partitions || {};
360
+ const group = groups[tab] || { total: 0, valid: 0, entries: [] };
361
+ const entries = group.entries || [];
362
+ const tabButton = (key, label, count) => React.createElement("button", {
363
+ key,
364
+ style: tab === key ? buttonStyle : buttonGhost,
365
+ onClick: () => setTab(key),
366
+ }, count === undefined ? label : label + "(" + count + ")");
367
+ const om = (digest && digest.omitted) || { tact: 0, topic: 0, wander: 0 };
320
368
  return React.createElement(
321
369
  "div",
322
370
  { style: listStyle },
323
- React.createElement("div", null, React.createElement("b", null, "处境切面"), React.createElement("pre", { style: { whiteSpace: "pre-wrap", margin: "2px 0", fontSize: 12 } }, data.tact || "(空)")),
324
- React.createElement("div", null, React.createElement("b", null, "话题切面"), React.createElement("pre", { style: { whiteSpace: "pre-wrap", margin: "2px 0", fontSize: 12 } }, data.topic || "(空)")),
371
+ React.createElement("div", { style: tabRow },
372
+ PARTITIONS.map((p) => tabButton(p.key, p.label, (groups[p.key] || {}).valid || 0)),
373
+ tabButton("__engine", "引擎室输入")),
374
+ tab === "__engine"
375
+ ? React.createElement("div", null,
376
+ React.createElement("div", { style: hintStyle }, digest
377
+ ? "共 " + digest.totalChars + " / " + digest.budgetChars + " 字符(" + (digest.withinBudget ? "在预算内" : "超预算") + ");按预算省略:处境 " + om.tact + " 条、话题 " + om.topic + " 条、兴趣 " + om.wander + " 条"
378
+ : "加载中…"),
379
+ React.createElement("div", { style: scrollBox },
380
+ React.createElement("div", null,
381
+ React.createElement("b", null, "处境切面"),
382
+ React.createElement("div", { style: entryBody }, (digest && digest.tact) || "(空)")),
383
+ React.createElement("div", null,
384
+ React.createElement("b", null, "话题切面"),
385
+ React.createElement("div", { style: entryBody }, (digest && digest.topic) || "(空)")),
386
+ React.createElement("div", null,
387
+ React.createElement("b", null, "兴趣切面"),
388
+ React.createElement("div", { style: entryBody }, (digest && digest.wander) || "(空)"))))
389
+ : React.createElement("div", null,
390
+ React.createElement("div", { style: hintStyle }, "有效 " + group.valid + " 条 / 历史 " + (group.total - group.valid) + " 条(过期条目不进引擎室输入,也不在这里列出)"),
391
+ entries.length === 0
392
+ ? React.createElement("div", { style: hintStyle }, "(这个分区还没有条目)")
393
+ : React.createElement("div", { style: scrollBox }, entries.map((e) => React.createElement("div", { key: e.id || (String(e.topic) + String(e.content).slice(0, 12)), style: entryBox },
394
+ React.createElement("div", { style: entryHead },
395
+ (e.topic || "") + "/" + (e.subTopic || "")
396
+ + " conf " + Number(e.confidence || 0).toFixed(2)
397
+ + (e.temporal ? " " + e.temporal : "")
398
+ + (e.lowActivity ? " 久未验证" : "")
399
+ + (e.updatedAt ? " 更新 " + String(e.updatedAt).slice(0, 10) : "")),
400
+ React.createElement("div", { style: entryBody }, e.content || ""))))),
325
401
  React.createElement("div", { style: rowStyle },
326
402
  React.createElement("button", { style: buttonGhost, onClick: () => { load(); } }, "刷新"),
327
403
  React.createElement("button", { style: buttonGhost, onClick: () => { void doExport(); } }, "导出 Markdown")),
328
- data.exported ? React.createElement("div", { style: hintStyle }, "已导出:" + data.exported) : null,
404
+ status ? React.createElement("div", { style: hintStyle }, status) : null,
329
405
  );
330
406
  }
331
407
 
@@ -339,10 +415,10 @@ window.__ModuleLoader__.load({
339
415
  React.useEffect(() => {
340
416
  rpc("config.get").then(setValue, (e) => setLoadErr(String(e).slice(0, 100)));
341
417
  }, []);
342
- const interval = value && Number(value.intervalMin) > 0 ? Number(value.intervalMin) : 20;
343
- const cap = value && Number(value.maxDailySend) > 0 ? Number(value.maxDailySend) : 3;
418
+ const interval = value && Number(value.intervalMin) > 0 ? Number(value.intervalMin) : 60;
419
+ const cap = value && Number(value.maxDailySend) > 0 ? Number(value.maxDailySend) : 10;
344
420
  const timeInject = value && value.timeInjectMin === 0 ? 0 : (value && Number(value.timeInjectMin) > 0 ? Number(value.timeInjectMin) : 25);
345
- const statusbar = !value || value.statusbar !== false;
421
+ const statusbar = !!value && value.statusbar === true;
346
422
  const idleMode = !!value && value.idleMode === true;
347
423
  const tokenSaver = !!value && value.tokenSaver === true;
348
424
  const psyEnabled = !!value && value.psyEnabled === true;
@@ -510,6 +586,11 @@ window.__ModuleLoader__.load({
510
586
  (v) => setLedgerMsg("已打开:" + v.path),
511
587
  (e) => setLedgerMsg("失败:" + String(e).slice(0, 80)),
512
588
  );
589
+ const [excerptMsg, setExcerptMsg] = React.useState("");
590
+ const openExcerpt = () => rpc("excerpt.open").then(
591
+ (v) => setExcerptMsg("已打开:" + v.path),
592
+ (e) => setExcerptMsg("失败:" + String(e).slice(0, 80)),
593
+ );
513
594
  return React.createElement(
514
595
  "div",
515
596
  { style: { padding: "2px 0" } },
@@ -525,6 +606,13 @@ window.__ModuleLoader__.load({
525
606
  React.createElement("button", { style: buttonStyle, onClick: () => { void openLedger(); } }, "一键打开账本"),
526
607
  React.createElement("span", { style: hintStyle }, ledgerMsg || "账本是心跳 agent 的待办与话题来源,可直接手编")),
527
608
  )),
609
+ React.createElement(Section, { title: "摘抄本" }, React.createElement(
610
+ "div",
611
+ null,
612
+ React.createElement("div", { style: rowStyle },
613
+ React.createElement("button", { style: buttonStyle, onClick: () => { void openExcerpt(); } }, "一键打开摘抄本"),
614
+ React.createElement("span", { style: hintStyle }, excerptMsg || "留句子和小事的地方:只增不减,直接手编也不会被覆盖")),
615
+ )),
528
616
  React.createElement(Section, { title: "周报" }, React.createElement(WeeklySection, null)),
529
617
  React.createElement(Section, { title: "换机迁移" }, React.createElement(MigrateSection, null)),
530
618
  React.createElement(Section, { title: "节律配置" }, React.createElement(ConfigSection, null)),
@@ -1,73 +1,74 @@
1
- {
2
- "heartbeat": {
3
- "intervalMin": 20,
4
- "idleMode": false,
5
- "archiveRotatedHome": true
6
- },
7
- "gate": {
8
- "maxDailySend": 3,
9
- "cooldownMinutes": 30,
10
- "quietHours": {
11
- "start": "01:00",
12
- "end": "08:00"
13
- }
14
- },
15
- "browse": {
16
- "windows": [
17
- {
18
- "start": "11:00",
19
- "end": "15:00"
20
- },
21
- {
22
- "start": "17:00",
23
- "end": "21:00"
24
- }
25
- ],
26
- "minIntervalHours": 4,
27
- "maxSeedsPerVisit": 2
28
- },
29
- "seeds": {
30
- "maxActive": 30,
31
- "ttlDays": {
32
- "news": 3,
33
- "fandom": 14,
34
- "scene": 60,
35
- "promise": 90
36
- },
37
- "coldBenchDays": 21,
38
- "archiveCap": 50,
39
- "retireAfterUsed": 2,
40
- "scoreWeights": {
41
- "freshness": 0.4,
42
- "unused": 0.3,
43
- "confidence": 0.3
44
- }
45
- },
46
- "profile": {
47
- "consolidation": {
48
- "minIntervalHours": 12,
49
- "inboxBacklog": 30
50
- },
51
- "partitionCap": 50,
52
- "maxOpsPerRun": 10,
53
- "confidenceCap": {
54
- "chat": 0.6,
55
- "screen": 0.4,
56
- "browse": 0.4
57
- },
58
- "volatileDays": 14,
59
- "stableLowActivityDays": 180,
60
- "psyEnabled": false
61
- },
62
- "observe": {
63
- "maxChars": 80,
64
- "perBeat": 10
65
- },
66
- "weekly": {
67
- "enabled": true
68
- },
69
- "retention": {
70
- "envPulseHours": 48,
71
- "decisionLogDays": 30
72
- }
73
- }
1
+ {
2
+ "heartbeat": {
3
+ "intervalMin": 60,
4
+ "idleMode": false,
5
+ "archiveRotatedHome": true
6
+ },
7
+ "gate": {
8
+ "maxDailySend": 10,
9
+ "cooldownMinutes": 30,
10
+ "quietHours": {
11
+ "start": "01:00",
12
+ "end": "08:00"
13
+ }
14
+ },
15
+ "browse": {
16
+ "windows": [
17
+ {
18
+ "start": "11:00",
19
+ "end": "15:00"
20
+ },
21
+ {
22
+ "start": "17:00",
23
+ "end": "21:00"
24
+ }
25
+ ],
26
+ "minIntervalHours": 4,
27
+ "maxSeedsPerVisit": 2
28
+ },
29
+ "seeds": {
30
+ "maxActive": 30,
31
+ "ttlDays": {
32
+ "news": 3,
33
+ "fandom": 14,
34
+ "scene": 60,
35
+ "promise": 90
36
+ },
37
+ "coldBenchDays": 21,
38
+ "archiveCap": 50,
39
+ "retireAfterUsed": 2,
40
+ "scoreWeights": {
41
+ "freshness": 0.4,
42
+ "unused": 0.3,
43
+ "confidence": 0.3
44
+ }
45
+ },
46
+ "profile": {
47
+ "consolidation": {
48
+ "minIntervalHours": 12,
49
+ "inboxBacklog": 30
50
+ },
51
+ "partitionCap": 50,
52
+ "maxOpsPerRun": 10,
53
+ "confidenceCap": {
54
+ "chat": 0.6,
55
+ "screen": 0.4,
56
+ "browse": 0.4
57
+ },
58
+ "volatileDays": 14,
59
+ "stableLowActivityDays": 180,
60
+ "psyEnabled": false
61
+ },
62
+ "observe": {
63
+ "maxChars": 80,
64
+ "perBeat": 10
65
+ },
66
+ "weekly": {
67
+ "enabled": true,
68
+ "keepReports": 3
69
+ },
70
+ "retention": {
71
+ "envPulseHours": 48,
72
+ "decisionLogDays": 30
73
+ }
74
+ }