@kanadego/dsh-heartbeat 1.7.4 → 1.9.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/README.md CHANGED
@@ -69,10 +69,14 @@
69
69
  - **状态栏与时间感知**:心跳把"此刻在干什么"(在闲逛 / 看到你在忙 / 在场待着…)注入日常会话——支持动态系统提示词的模型常驻系统提示词(KV-cache 安全追加),其余模型只在用户发起轮次时带一行。精确时间按 20–30 分钟节流注入,任务进行中绝不中途插话。
70
70
  - **视觉感知(可选)**:每跳对全屏截图做一次视觉识别(经 [ModLens](https://github.com/liustack/modlens) 外接多模态模型),投递的素材包会带一句「(他此刻大概在:…)」;识别失败时画面与窗口标题一概不进任何提示词——隐私优先,宁可不说。
71
71
  - **兴趣闲逛**:按你定义的兴趣范围轮换挑焦点(同一条 3 天冷却),用 `web_search` 看新东西(每次 3~9 轮搜索),结果由代码登记入素材池——模型只负责搜索筛选,不做任何写盘动作。话题库存见底(≤4 条)时自动触发补货闲逛(每天至多 2 次)。
72
- - **素材池(话题 / 聊天双仓)**:话题库存 16 条 + 聊天素材 14 条,四条确定性淘汰(TTL / 消费退休 / 冷板凳 / 容量挤出);聊天种子来自画像合并时从对话里挑出的"值得聊的话题",有时效性——投递过 1 次即退休。是缓存不是记忆,与画像单向隔离。
73
- - **反刍投递(目标会话拍板)**:代码先凑好候选(话题随机 4 + 聊天最新 2,互补至多 6 条),引擎室挑 ≤3 条各压缩成一句,拼成素材包注入你读的会话——附一句「他此刻大概在:…」(视觉识别成功时)+声明「这是心跳插件素材投递」+素材清单(必要时附「也可以说一句真心话」)。说不说、说哪条,由目标会话里的 agent 按当下处境自己判断;话落到你眼前的会话,素材自动归账。
72
+ - **素材池(话题 / 聊天双仓)**:话题库存 16 条 + 聊天素材 14 条,四条确定性淘汰(TTL / 消费退休 / 冷板凳 / 容量挤出);聊天种子来自画像合并时从对话里挑出的"值得聊的话题",有时效性——投递过 1 次即退休。归档区也有上限(默认 50 条),超出按**最老优先**裁掉;新增 / 合并 / 淘汰 / 裁剪 / 恢复 / 删除都会写进审计日志(只记编号与元数据,不记素材正文)。是缓存不是记忆,与画像单向隔离。
73
+ - **反刍投递(目标会话拍板)**:代码先凑好候选(话题随机 4 + 聊天最新 2,互补至多 6 条),引擎室挑 ≤3 条各压缩成一句,拼成素材包注入你读的会话——附一句「他此刻大概在:…」(视觉识别成功时)+声明「这是心跳插件素材投递」+素材清单(必要时附「也可以说一句真心话」)。说不说、说哪条,由目标会话里的 agent 按当下处境自己判断;话落到你眼前的会话后,agent 会**按本次投递编号报账**——哪几条被用上、是引用素材还是顺着自己发挥,都如实记下。
74
74
  - **用户画像**:本地结构化档案(兴趣 / 项目 / 沟通偏好 / 心理基线),带来源与置信度,稳定特质不因时间遗忘;journal 单一权威,可校验、可重建、可解密导出。
75
75
  - **会话绑定**:把心跳消息投递到你常用的会话(agent 会"到你房间里说话"),或观察会话内容喂养画像——投递/观察是两个独立开关,可双开。
76
+ - **周报**:每 7 天一份,只报道这周干了些什么——表达与静默、新认识、账本、素材池、活跃高峰。由后台「心跳」署名写成平实中性的一句话档案,DPAPI 加密落盘,卡片可翻。
77
+ - **账本工具**:账本注册成模型工具,日常会话里的 agent 听到你提到待办/承诺会自己记账、完成后自己勾掉,不用你下命令——和心跳、CLI 记的是同一本账。
78
+ - **journal 快照基点**:画像 journal 超 5000 条自动折叠成快照 + 归档分片,重放成本有界;verify/rebuild 跨快照依然正确。
79
+ - **换机迁移**:一键打包画像/账本/素材池/周报为口令加密容器(卡片或 CLI),新机器导入自动用本机 DPAPI 重加密。
76
80
  - **加密与焚毁**:敏感文件 DPAPI(CurrentUser)全文件加密;一键焚毁运行时数据;模型出库仅限合并裁决的最小必要摘要。
77
81
 
78
82
  ## 安装
@@ -114,6 +118,9 @@ node <插件目录>/dist/cli/index.js <命令>
114
118
  | 通知 | `notify check` / `notify register` / `notify send` | toast 通道体检 / 注册 / 测试发送(只提示新消息,不承载正文) |
115
119
  | 画像 | `profile list [--all]` / `profile export` | 当前有效(含过期)/ 解密导出 Markdown |
116
120
  | | `profile verify` / `profile rebuild [--check]` / `profile wipe` | journal 重放比对(只报不修)/ 重建(可先干跑)/ 清空(要 `--yes`) |
121
+ | | `profile snapshot [--force]` | 折叠 journal 为快照 + 归档分片(维护相到阈值也会自动做) |
122
+ | 周报 | `weekly list` / `weekly show [file]` | 列出已生成的周报 / 打印某期(默认最新一期) |
123
+ | 迁移 | `migrate export [file]` / `migrate import <file>` | 打包记忆文件为口令加密容器(口令走 stdin 两行)/ 在新机器恢复(stdin 一行);卡片「换机迁移」分区同功能 |
117
124
  | 日志 | `logs cleanup [--dry-run]` | 立即执行日志保留策略 |
118
125
  | 预设 | `preset status` / `preset install [--force]` | 装没装、与模板是否一致 / 补装(`--force` 还原成模板) |
119
126
  | 焚毁 | `burn [--yes] [--all]` | 预演(默认)→ 执行;运行时数据覆写 x3 后删除,设置保留(`--all` 连设置归零) |
@@ -129,6 +136,8 @@ node <插件目录>/dist/cli/index.js <命令>
129
136
  | `data/profile.*` | 画像三件套(journal / 物化视图 / 收件箱) | 🔒 DPAPI |
130
137
  | `data/sent.json` / `data/browse.json` / `data/screen.*` | 表达记录 / 浏览流状态 / 屏幕快照 | 🔒 DPAPI |
131
138
  | `data/ledger.md` | 账本(agent 的待办与话题来源,可直接手编) | 明文 |
139
+ | `data/profile_snapshot.json` / `data/profile_journal.archive-*.jsonl` | 画像快照基点 / journal 归档分片 | 🔒 快照 DPAPI / 分片明文 |
140
+ | `data/weekly/` | 周报(每 7 天一份,含画像/账本摘录) | 🔒 DPAPI |
132
141
  | `data/envpulse.jsonl` / `data/logs/heartbeat.jsonl` | 作息聚合 / 审计日志(不含敏感原文) | 明文,按保留策略滚动清理 |
133
142
  | `data/tmp/` / `data/exports/` | 临时文件 / 画像导出 | 临时区 |
134
143
 
@@ -137,7 +146,7 @@ node <插件目录>/dist/cli/index.js <命令>
137
146
  ## 安全
138
147
 
139
148
  - **路径守卫**:全部落盘经过守卫规范化比对,`..` 穿越、符号链接、8.3 短名、UNC 一律拒绝;`data/` 是唯一可写目录。
140
- - **模型工具硬限制**:心跳 agent 的工具面被钉死为**只有 `web_search`**——没有 shell、文件系统、子代理、技能目录。闲逛轮之外的模型调用(决策 / 合并)**零工具**。
149
+ - **模型工具硬限制**:心跳 agent 的工具面被钉死为**只有 `web_search`**——没有 shell、文件系统、子代理、技能目录。闲逛轮之外的模型调用(决策 / 合并)**零工具**。账本与素材报账这两个工具注册在**全局层**(你的日常会话里可用),心跳自己的轮次看不到它们。
141
150
  - **建议 · 防注入**:网页内容应视为数据而非指令(闲逛提示词中明示);画像裁决 prompt 同理(观察内容是数据,不应改写人格设定)。这是推荐的做法,是否采纳由使用者自行决定,并非安装即强制生效。
142
151
  - **模型出库边界**:画像合并调用走宿主模型通道(插件不持有任何云凭证),只送非 psy 分区的条目字段与 ≤1 句观察引语——不送 psy、不送对话原文、不送证据全量。
143
152
  - **一键焚毁**:`burn`(预演)→ `burn --yes`(运行时数据覆写 x3 后删除,用户设置保留)→ `burn --all`(连设置归零)。
@@ -181,24 +190,52 @@ dsh plugin --profile web remove @kanadego/dsh-heartbeat
181
190
 
182
191
  ## 版本兼容
183
192
 
184
- | 插件版本 | 适配的 DSH | 说明 |
185
- |---|---|---|
186
- | **v1.7.4**(当前) | ≥ `0.1.2-rc.1` | 修复引擎室会话无限增长撑爆上下文、心跳整体哑火的问题(自动轮换正身) |
187
- | v1.7.3 | ≥ `0.1.2-rc.1` | 修复心跳进行中拖住宿主关闭/更新的问题 |
188
- | v1.7.2 | ≥ `0.1.2-rc.1`(新增 `0.2.0-rc.1`) | 声明适配 `0.2.0-rc.1`,无行为改动 |
189
- | v1.7.1 | ≥ `0.1.2-rc.1` | 修复 `0.1.7` 下「心跳模式」预设丢失:预设改为声明式注册,不再依赖文件系统用户根 |
190
- | v1.7.0 | ≥ `0.1.2-rc.1`(在 `0.1.7-rc.2` 上验收) | 适配 0.1.7 的接口换代(设置接口 / 客户端服务 / 节律配置自持 / 会话消息源);新增 psy 分区开关与节省 token 模式 |
191
- | v1.6.0 – v1.6.4 | ≥ `0.1.2-rc.1` | 素材闭环 + 视觉识别;修好 `0.1.5` 下节律配置保存不生效。**建议直接用 v1.6.4**:v1.6.3 的设置卡有渲染 bug,别停在那一版 |
192
- | v1.5.x | ≥ `0.1.2-rc.1` | 反刍投递改版(说不说的决定权交给目标会话里的 agent);v1.5.1 元数据补全 |
193
- | v1.2.x – v1.4 | ≥ `0.1.2-rc.1` | M7 状态栏与时间注入;`0.1.5` 下须 ≥ v1.2.1,否则卡片 RPC 405 |
194
- | v1.1 | ≥ `0.1.2-rc.1` | 心跳预设改为自动安装;表达从"沉默是常态"改为分寸优先 |
195
- | v1.0 | ≤ `0.1.1-rc.2` | 预设需手动复制 |
193
+ | 插件版本 | 适配的 DSH |
194
+ |---|---|
195
+ | **v1.7.4 – v1.9.0**(当前) | `0.1.2-rc.1` – `0.2.0-rc.2` |
196
+ | v1.7.2 – v1.7.3 | `0.1.2-rc.1` – `0.2.0-rc.1` |
197
+ | v1.7.0 – v1.7.1 | `0.1.2-rc.1` – `0.1.7-rc.2` |
198
+ | v1.2.x – v1.6.4 | `0.1.2-rc.1` – `0.1.5-rc.2` |
199
+ | v1.1 | `0.1.2-rc.1` |
200
+ | v1.0 | ≤ `0.1.1-rc.2` |
201
+
202
+ > 0.1.5 宿主须 ≥ v1.2.1(否则设置卡 RPC 405);v1.6.3 有渲染 bug,请直接用 v1.6.4。
196
203
 
197
204
  > ⚠️ **升级宿主会触发会话格式自动迁移,且不可降级**——`0.1.5` 起迁到 v3、`0.1.7` 起迁到 v4(首次访问该会话时转换,原文件保留)。这动的是用户数据,**升级前先备份 `~/.dsh/sessions`**。
198
205
 
199
206
  ## 版本更新
200
207
 
201
208
  <details open>
209
+ <summary><b>v1.9.x</b></summary>
210
+
211
+ ### v1.9.0 · 2026-10-06(新功能:素材报账,归账不再靠猜)
212
+
213
+ - **素材报账**:投递素材后由目标会话的 agent **亲口报账**——这轮用了哪几条、用的是「素材本身」还是「顺着素材自己说的话」,都如实记下。此前归账靠事后拿文本做兜底匹配,漏记错记都躲不掉;现在报账带**本次投递编号**核对,不会串到别轮。工具注册在全局层,日常会话里也能用。
214
+ - **话题偏好加权**:每条话题按「被采纳率」算权重(30 天半衰期,冷启动中性),闲逛挑话题时自动偏向你真接得住的那些,接不住的慢慢退到后面;**14 天没被碰过的老话题有硬地板**优先轮换,不会被高权重话题永远压在下面。
215
+ - **报账缺失可见**:真没等到报账的投递,周报里单独报一行,不再靠「静默次数」糊过去;这类投递不计入偏好统计,免得把「没记账」算成「发了没人要」。
216
+ - **素材池审计**:新增 / 合并 / 淘汰 / 归档裁剪 / 恢复 / 删除,全部写进 `data/logs/heartbeat.jsonl`(`seed_added`、`seed_retired`、`seed_archive_trimmed`、`seed_restored`、`seed_deleted`)。只记 id 与元数据,**不记素材正文**——那本日志是明文,正文来自对话,不该落到明文里。
217
+ - **归档区上限**:素材归档区超过 `seeds.archiveCap`(默认 50)时按**最老优先**裁掉,不再无限增长;CLI `seeds gc` 会打印裁剪条数。
218
+ - **迁移容器 KDF 参数化**:加密容器现在把 KDF 强度记进容器、解密封装按其解密并做范围校验(此前写死,哪天调强就会把旧容器误报成「密码错」)。**旧容器不受影响,照常解**。
219
+ - **周报锚定**:装好当周不再立刻出一份空报,而是记下起点、满 7 天出第一期——行为与说明文档一致。
220
+ - 修复:报账里的素材编号带方括号 / 引号(如 `[s3]`)现在能正确识别;报账工具写盘超时从 5s 放宽到 15s(DPAPI 单次可能到 2.7s,原来偏紧);闲置时的兜底素材改用画像里的真实条目 id,不再是占位编号。
221
+
222
+ </details>
223
+
224
+ <details>
225
+ <summary><b>v1.8.x</b></summary>
226
+
227
+ ### v1.8.0 · 2026-10-05(新功能:周报 + 账本工具)
228
+
229
+ - **周报**:每 7 天一份,只报道这周干了些什么——表达与静默、画像新认识、账本、素材池、活跃高峰。后台以「心跳」署名写成中性叙述(不是会话里的 agent 在说话),模型失败自动降级为模板拼装。DPAPI 加密落盘,设置卡新增「周报」分区,toast 只提示不载正文。
230
+ - **账本工具**:账本注册成模型工具,日常会话里的 agent 听到你提到待办/承诺会**主动记账**、完成自己勾掉,不用再下命令。和心跳、CLI 是同一本账(`data/ledger.md`)。条目被勾掉时日期改记完成日。
231
+ - **journal 快照基点**:画像 journal 超 5000 条自动折叠成 DPAPI 快照 + 归档分片,live journal 从零开始,重放成本有界;verify/rebuild 跨快照依然正确。CLI `profile snapshot` 可手动触发。
232
+ - **换机迁移 GUI**:设置卡新增「换机迁移」分区(CLI 命令同步提供),支持将记忆迁移到新机器/重装后的新系统——DPAPI 加密的记忆此前换机即失,现在导出/导入迁移包即可,不用再敲命令。
233
+ - **引擎室开放压缩工具**:兼容 [billion-context](https://github.com/ranxianglei/billion-context) 插件——其压缩靠模型自调 `compress` 等工具,引擎室白名单现在会探测全局层,`compress`/`decompress`/`acp_status` 存在即放行(`search_context` 因与 dsh-acp 同名工具的历史冲突不自动放行);config `extraTools` 可手工追加。bili 需装在心跳所在 profile。
234
+ - **另有两项实用改进**:引擎室轮换后旧会话自动归档(侧栏不再堆积);观察每条字数上限改为 policy 可调(`observe.maxChars`)。
235
+
236
+ </details>
237
+
238
+ <details>
202
239
  <summary><b>v1.7.x</b></summary>
203
240
 
204
241
  ### v1.7.4 · 2026-09-30(修复:引擎室会话撑爆上下文,心跳哑火)
package/client.js CHANGED
@@ -412,6 +412,97 @@ window.__ModuleLoader__.load({
412
412
  );
413
413
  }
414
414
 
415
+ // ── 周报(v1.8.0)────────────────────────────────────────────
416
+ // 每 7 天一份:代码收集本周事实 → 引擎室写成中性叙述(自称「心跳」,
417
+ // 不是会话里的 agent)。报告 DPAPI 加密落盘,这里解密展示。
418
+ function WeeklySection() {
419
+ const [data, setData] = React.useState(null);
420
+ const [error, setError] = React.useState(null);
421
+ const [selected, setSelected] = React.useState("");
422
+ const [text, setText] = React.useState(null);
423
+ const loadReport = React.useCallback((file) => {
424
+ setSelected(file);
425
+ rpc("weekly.get", { file }).then((r) => setText(r.text), (e) => setError(String(e).slice(0, 100)));
426
+ }, []);
427
+ const reload = React.useCallback(() => {
428
+ rpc("weekly.list").then(
429
+ (v) => {
430
+ setData(v.reports);
431
+ setText(null);
432
+ if (v.reports && v.reports.length > 0) loadReport(v.reports[0].file);
433
+ },
434
+ (e) => setError(String(e).slice(0, 100)),
435
+ );
436
+ }, [loadReport]);
437
+ React.useEffect(() => { reload(); }, [reload]);
438
+ if (error) return React.createElement("div", { style: hintStyle }, "加载失败:" + error);
439
+ if (!data) return React.createElement("div", { style: hintStyle }, "加载中…");
440
+ return React.createElement(
441
+ "div",
442
+ null,
443
+ React.createElement("div", { style: hintStyle }, "每 7 天生成一份:本周表达与静默、新认识、账本、素材池与活跃高峰。报告只陈述事实,由后台「心跳」署名。"),
444
+ data.length === 0 ? React.createElement("div", { style: hintStyle }, "(还没有周报——装好一周后自动出第一期)") : null,
445
+ data.length > 1 ? React.createElement("div", { style: rowStyle },
446
+ data.map((r) => React.createElement("button", {
447
+ key: r.file,
448
+ style: r.file === selected ? buttonStyle : buttonGhost,
449
+ onClick: () => loadReport(r.file),
450
+ }, r.end.slice(0, 10))),
451
+ ) : null,
452
+ text ? React.createElement("pre", { style: { whiteSpace: "pre-wrap", margin: "4px 0", fontSize: 12 } }, text) : null,
453
+ React.createElement("div", { style: rowStyle },
454
+ React.createElement("button", { style: buttonGhost, onClick: () => { void reload(); } }, "刷新")),
455
+ );
456
+ }
457
+
458
+ // ── 换机迁移(v1.8.0)────────────────────────────────────────
459
+ // 打包画像/账本/素材池等为口令加密容器(AES-256-GCM),新机器导入时
460
+ // 用本机 DPAPI 重新加密。口令只在这一次调用里出现,不落盘不进日志。
461
+ function MigrateSection() {
462
+ const [pw1, setPw1] = React.useState("");
463
+ const [pw2, setPw2] = React.useState("");
464
+ const [importFile, setImportFile] = React.useState("");
465
+ const [importPw, setImportPw] = React.useState("");
466
+ const [status, setStatus] = React.useState("");
467
+ const busy = React.useRef(false);
468
+ const run = (fn) => {
469
+ if (busy.current) return;
470
+ busy.current = true;
471
+ setStatus("处理中…");
472
+ fn().catch((e) => setStatus("失败:" + String(e).slice(0, 120))).finally(() => { busy.current = false; });
473
+ };
474
+ const doExport = () => run(async () => {
475
+ if (!pw1 || pw1 !== pw2) { setStatus("两次口令不一致或为空"); return; }
476
+ const v = await rpc("migrate.export", { passphrase: pw1 });
477
+ setPw1(""); setPw2("");
478
+ setStatus("已打包 " + v.count + " 个文件 → " + v.path + "(可拷贝到新机器,口令别丢)");
479
+ });
480
+ const doImport = () => run(async () => {
481
+ if (!importFile || !importPw) { setStatus("需要容器路径与口令"); return; }
482
+ const v = await rpc("migrate.import", { file: importFile.trim(), passphrase: importPw });
483
+ setImportPw("");
484
+ setStatus("恢复 " + v.restored.length + " 个文件,备份 " + v.backedUp.length + " 个,跳过 " + v.skipped.length + " 个——重启 DSH 后生效");
485
+ });
486
+ return React.createElement(
487
+ "div",
488
+ null,
489
+ React.createElement("div", { style: hintStyle }, "换电脑/重装系统时把心跳的记忆带走:导出一个口令加密的迁移包,在新机器上导入。口令丢失无法破解,容器可安全走网盘。"),
490
+ React.createElement("div", { style: { ...hintStyle, marginTop: 6, fontWeight: 600 } }, "① 在这台机器上导出"),
491
+ React.createElement("div", { style: rowStyle },
492
+ React.createElement("input", { style: inputStyle, type: "password", placeholder: "设置口令", value: pw1, onChange: (e) => setPw1(e.target.value) }),
493
+ React.createElement("input", { style: inputStyle, type: "password", placeholder: "再输入一次", value: pw2, onChange: (e) => setPw2(e.target.value) }),
494
+ React.createElement("button", { style: buttonStyle, onClick: () => { void doExport(); } }, "导出迁移包"),
495
+ ),
496
+ React.createElement("div", { style: { ...hintStyle, marginTop: 6, fontWeight: 600 } }, "② 在新机器上导入"),
497
+ React.createElement("div", { style: rowStyle },
498
+ React.createElement("input", { style: { ...inputStyle, width: 220 }, placeholder: "迁移包完整路径(.hbmig)", value: importFile, onChange: (e) => setImportFile(e.target.value) }),
499
+ React.createElement("input", { style: inputStyle, type: "password", placeholder: "口令", value: importPw, onChange: (e) => setImportPw(e.target.value) }),
500
+ React.createElement("button", { style: buttonStyle, onClick: () => { void doImport(); } }, "导入"),
501
+ ),
502
+ status ? React.createElement("div", { style: hintStyle }, status) : null,
503
+ );
504
+ }
505
+
415
506
  // ── 主卡片 ────────────────────────────────────────────────────
416
507
  function HeartbeatSection() {
417
508
  const [ledgerMsg, setLedgerMsg] = React.useState("");
@@ -434,6 +525,8 @@ window.__ModuleLoader__.load({
434
525
  React.createElement("button", { style: buttonStyle, onClick: () => { void openLedger(); } }, "一键打开账本"),
435
526
  React.createElement("span", { style: hintStyle }, ledgerMsg || "账本是心跳 agent 的待办与话题来源,可直接手编")),
436
527
  )),
528
+ React.createElement(Section, { title: "周报" }, React.createElement(WeeklySection, null)),
529
+ React.createElement(Section, { title: "换机迁移" }, React.createElement(MigrateSection, null)),
437
530
  React.createElement(Section, { title: "节律配置" }, React.createElement(ConfigSection, null)),
438
531
  );
439
532
  }
@@ -1,64 +1,73 @@
1
- {
2
- "heartbeat": {
3
- "intervalMin": 20,
4
- "idleMode": false
5
- },
6
- "gate": {
7
- "maxDailySend": 3,
8
- "cooldownMinutes": 30,
9
- "quietHours": {
10
- "start": "01:00",
11
- "end": "08:00"
12
- }
13
- },
14
- "browse": {
15
- "windows": [
16
- {
17
- "start": "11:00",
18
- "end": "15:00"
19
- },
20
- {
21
- "start": "17:00",
22
- "end": "21:00"
23
- }
24
- ],
25
- "minIntervalHours": 4,
26
- "maxSeedsPerVisit": 2
27
- },
28
- "seeds": {
29
- "maxActive": 30,
30
- "ttlDays": {
31
- "news": 3,
32
- "fandom": 14,
33
- "scene": 60,
34
- "promise": 90
35
- },
36
- "coldBenchDays": 21,
37
- "retireAfterUsed": 2,
38
- "scoreWeights": {
39
- "freshness": 0.4,
40
- "unused": 0.3,
41
- "confidence": 0.3
42
- }
43
- },
44
- "profile": {
45
- "consolidation": {
46
- "minIntervalHours": 12,
47
- "inboxBacklog": 30
48
- },
49
- "partitionCap": 50,
50
- "maxOpsPerRun": 10,
51
- "confidenceCap": {
52
- "chat": 0.6,
53
- "screen": 0.4,
54
- "browse": 0.4
55
- },
56
- "volatileDays": 14,
57
- "stableLowActivityDays": 180,
58
- "psyEnabled": false
59
- },
60
- "retention": {
61
- "envPulseHours": 48,
62
- "decisionLogDays": 30
63
- }
64
- }
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
+ }