@kanadego/dsh-heartbeat 1.7.1 → 1.7.3
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 +62 -13
- package/cordis.patch.yml +1 -1
- package/dist/index.js +130 -37
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -183,7 +183,9 @@ dsh plugin --profile web remove @kanadego/dsh-heartbeat
|
|
|
183
183
|
|
|
184
184
|
| 插件版本 | 适配的 DSH | 说明 |
|
|
185
185
|
|---|---|---|
|
|
186
|
-
| **v1.7.
|
|
186
|
+
| **v1.7.3**(当前) | ≥ `0.1.2-rc.1` | 修复心跳进行中拖住宿主关闭/更新的问题 |
|
|
187
|
+
| v1.7.2 | ≥ `0.1.2-rc.1`(新增 `0.2.0-rc.1`) | 声明适配 `0.2.0-rc.1`,无行为改动 |
|
|
188
|
+
| v1.7.1 | ≥ `0.1.2-rc.1` | 修复 `0.1.7` 下「心跳模式」预设丢失:预设改为声明式注册,不再依赖文件系统用户根 |
|
|
187
189
|
| v1.7.0 | ≥ `0.1.2-rc.1`(在 `0.1.7-rc.2` 上验收) | 适配 0.1.7 的接口换代(设置接口 / 客户端服务 / 节律配置自持 / 会话消息源);新增 psy 分区开关与节省 token 模式 |
|
|
188
190
|
| v1.6.0 – v1.6.4 | ≥ `0.1.2-rc.1` | 素材闭环 + 视觉识别;修好 `0.1.5` 下节律配置保存不生效。**建议直接用 v1.6.4**:v1.6.3 的设置卡有渲染 bug,别停在那一版 |
|
|
189
191
|
| v1.5.x | ≥ `0.1.2-rc.1` | 反刍投递改版(说不说的决定权交给目标会话里的 agent);v1.5.1 元数据补全 |
|
|
@@ -195,26 +197,40 @@ dsh plugin --profile web remove @kanadego/dsh-heartbeat
|
|
|
195
197
|
|
|
196
198
|
## 版本更新
|
|
197
199
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
-
|
|
200
|
+
<details open>
|
|
201
|
+
<summary><b>v1.7.x</b></summary>
|
|
202
|
+
|
|
203
|
+
### v1.7.3 · 2026-09-29(修复:心跳拖住 DSH 关闭)
|
|
204
|
+
|
|
205
|
+
修复心跳正在跑时 DSH 关不掉的问题——此前桌面端更新若恰逢一跳在跑,宿主等心跳结束等不到,更新会报「未能安全停止任务」。现在收到关闭信号,心跳会立刻停下手里这一跳,不再拖住宿主。
|
|
206
|
+
|
|
207
|
+
### v1.7.2 · 2026-09-29(适配 DSH `0.2.0-rc.1`)
|
|
208
|
+
|
|
209
|
+
声明适配 `0.2.0-rc.1`,无行为改动。
|
|
210
|
+
|
|
211
|
+
### v1.7.1 · 2026-09-25(修复「心跳模式」预设丢失)
|
|
212
|
+
|
|
213
|
+
**修掉升级 0.1.7 后心跳预设静默失效的问题**:0.1.7 起宿主不再从文件系统用户目录加载 agent 预设,旧的安装方式失效,症状是审计出现 `preset_install skipped-no-root`、界面报 `Unknown agent preset: heartbeat`、闲逛轮报 `tools.restrict unknown web_search`——预设没挂上,心跳 agent 成了"裸 agent",连 `web_search` 都看不见。
|
|
214
|
+
|
|
215
|
+
改为在插件的安装清单里**声明式**注册预设(与官方 standard 预设同机制),重启即注册,「心跳模式」重新出现在 Agent 预设页。`0.1.5` 及更早的宿主仍走原来的文件安装路径,两边都不断。
|
|
216
|
+
|
|
217
|
+
- 预设内容与原先一致:上下文折叠组 + 唯一模型工具 `web_search`(不抓正文);**无 persona 行**——部署人格与心跳提示词已足够,不能被 "You are a coding agent" 盖掉。
|
|
218
|
+
- 升级后重启 DSH 即可,无需手动操作;本版读取的是内置声明,若 `~/.dsh/.agent-presets` 下那份是你手改过的,它不再生效。
|
|
219
|
+
|
|
206
220
|
### v1.7.0 · 2026-09-24
|
|
207
221
|
|
|
208
222
|
- **现已适配 `0.1.7-rc.2`**(实机验收通过)
|
|
209
223
|
- **设置接口**:0.1.7 换掉了插件配置界面的机制(改由宿主根据插件声明的字段自动生成表单),旧写法会失效。心跳现在自动探测宿主是哪一代,从三代接口里挑能用的——你跑哪版 DSH 都不用改配置。
|
|
210
224
|
- **客户端服务**:0.1.7 删除了一个客户端旧服务,插件若仍声明它,整个 Web 界面会加载失败(症状:打开只看到「Failed to load plugins」,聊天区都进不去)。已移除该声明;设置卡其余分区不受影响。
|
|
211
225
|
- **节律配置**:不再借宿主的设置接口读写,卡片编辑器直接写插件自己的配置文件,**点保存立即生效**(改间隔会当场重排下一跳),不用重启 DSH。优先级(低 → 高):出厂默认 < 用户设置(`data/settings/ui.json`)< 宿主 patch 覆盖。
|
|
212
|
-
- **会话消息源**:0.1.7 的会话日志升级到 V4,不再接受旧版通用的消息来源标记——旧写法会让心跳轮次整轮写入失败。心跳已改用自有的来源标记,引擎室轮次恢复正常。
|
|
226
|
+
- **会话消息源**:0.1.7 的会话日志升级到 V4,不再接受旧版通用的消息来源标记——旧写法会让心跳轮次整轮写入失败。心跳已改用自有的来源标记,引擎室轮次恢复正常。
|
|
213
227
|
- **psy 分区开关**:设置卡新增 psy 开关(默认关)。开启后画像合并才收 psy 分区条目;关闭时相关 ADD 一律拒收。值写在用户 policy 层(data/settings/policy.json),即时生效。
|
|
214
228
|
- **节省 token 模式**:设置卡新增开关(默认关)。开启后,你离开(键盘/鼠标闲置 ≥30 分钟)或 Windows 锁屏时整跳暂停——不采维护、不闲逛、不反刍、不投递,一个 token 都不花;回来自动恢复。睡眠无需检测:睡眠中定时器本来不走,唤醒后闲置时长已覆盖睡眠期。探测失败一律放行(绝不因探测故障误暂停)。
|
|
215
229
|
|
|
230
|
+
</details>
|
|
231
|
+
|
|
216
232
|
<details>
|
|
217
|
-
<summary><b
|
|
233
|
+
<summary><b>v1.6.x</b></summary>
|
|
218
234
|
|
|
219
235
|
### v1.6.4 · 2026-09-20
|
|
220
236
|
|
|
@@ -252,6 +268,11 @@ dsh plugin --profile web remove @kanadego/dsh-heartbeat
|
|
|
252
268
|
- **UI**:心跳状态卡新增「插件版本」一行。
|
|
253
269
|
- 测试 163 条全绿。
|
|
254
270
|
|
|
271
|
+
</details>
|
|
272
|
+
|
|
273
|
+
<details>
|
|
274
|
+
<summary><b>v1.5.x</b></summary>
|
|
275
|
+
|
|
255
276
|
### v1.5.1 · 2026-09-16
|
|
256
277
|
|
|
257
278
|
**元数据补全,无行为改动**——只影响安装与生态对接:
|
|
@@ -268,6 +289,11 @@ dsh plugin --profile web remove @kanadego/dsh-heartbeat
|
|
|
268
289
|
- **素材包三段式投递**:①「这是心跳插件素材投递…」声明 ②素材清单 ③(3 条里 ≥2 条用过头时)「也可以说一句真心话」。目标会话里的 agent 按当下处境自己判断说不说、说哪条,话落到你读的会话,素材自动归账。
|
|
269
290
|
- **表达提取健壮化**:修复素材投递偶发 `non-Chinese output discarded`——目标会话 agent 收到素材包后爱调工具时,末行落在收尾标签上被误拦。v1.5 起跳过工具收尾标签、取最后一个含中文的行,整段无中文才拒。
|
|
270
291
|
|
|
292
|
+
</details>
|
|
293
|
+
|
|
294
|
+
<details>
|
|
295
|
+
<summary><b>v1.4.x</b></summary>
|
|
296
|
+
|
|
271
297
|
### v1.4.0 · 2026-09-13
|
|
272
298
|
|
|
273
299
|
**兴趣范围 / 浏览时段卡片管理**(决策 D22):
|
|
@@ -276,6 +302,11 @@ dsh plugin --profile web remove @kanadego/dsh-heartbeat
|
|
|
276
302
|
- **首编继承出厂**:第一次成功编辑时,出厂兴趣与排程被完整复制进用户层再应用改动——出厂条目零丢失,此后卡片即唯一管理入口;只打开卡片或校验失败不会创建用户层;
|
|
277
303
|
- 校验护栏:兴趣 ≤60 字 / 去重 / ≤32 条;窗口同日起始<结束 / 1–6 个 / 两两不重叠。
|
|
278
304
|
|
|
305
|
+
</details>
|
|
306
|
+
|
|
307
|
+
<details>
|
|
308
|
+
<summary><b>v1.3.x</b></summary>
|
|
309
|
+
|
|
279
310
|
### v1.3.x · 2026-09-13
|
|
280
311
|
|
|
281
312
|
**M7:状态栏与自研时间注入**——心跳的"在场感"从引擎室走进日常会话:
|
|
@@ -285,23 +316,41 @@ dsh plugin --profile web remove @kanadego/dsh-heartbeat
|
|
|
285
316
|
- **状态文本零时间词(D21)**:时间感知归时间注入,状态栏只承载场景,把 KV-cache 成本钉在"场景变化"粒度;
|
|
286
317
|
- UI:节律配置新增时间注入间隔与状态栏开关;v1.3.1 按评审修正了能力探测性能与轮次起源判定。
|
|
287
318
|
|
|
319
|
+
</details>
|
|
320
|
+
|
|
321
|
+
<details>
|
|
322
|
+
<summary><b>v1.2.x</b></summary>
|
|
323
|
+
|
|
288
324
|
### v1.2.x · 2026-09-11/12
|
|
289
325
|
|
|
290
326
|
适配 DSH `0.1.5-rc.2`:诊断兼容 `assistant/attempt` 事件形态(C19)、会话格式 v3 迁移适配(C20)、新增旧会话修复工具 `repair-v0-members.mjs`;v1.2.1 修复 0.1.5 下卡片 RPC 整体报 405(自定义通道在严格服务解析下不可用,迁移为 `/api` 精确路由,C21)。
|
|
291
327
|
|
|
328
|
+
</details>
|
|
329
|
+
|
|
330
|
+
<details>
|
|
331
|
+
<summary><b>v1.1.x</b></summary>
|
|
332
|
+
|
|
292
333
|
### v1.1 · 2026-09-10
|
|
293
334
|
|
|
294
335
|
适配 DSH `0.1.2-rc.1`:`Session.events` 移除、模型路由需显式传参、client 注册 id 强校验、会话标题投影迁移等六项宿主契约变化全数适配;**心跳预设改为首次启动自动安装**(一条 `dsh plugin add` 完成部署);表达从"沉默是常态"改为**分寸优先**(默认倾向开口);投递目标自动拉活;会话绑定投递/观察双开。
|
|
295
336
|
|
|
337
|
+
</details>
|
|
338
|
+
|
|
339
|
+
<details>
|
|
340
|
+
<summary><b>v1.0.x</b></summary>
|
|
341
|
+
|
|
296
342
|
### v1.0.0 · 2026-09-05
|
|
297
343
|
|
|
298
344
|
首个正式版:七相主循环、素材池 / 画像 / 闸门、DPAPI 加密与焚毁、审计留痕、设置页卡片。
|
|
299
345
|
|
|
346
|
+
</details>
|
|
347
|
+
|
|
300
348
|
<details>
|
|
301
|
-
<summary
|
|
349
|
+
<summary><b>v0.x(脚本外挂时代)</b></summary>
|
|
350
|
+
|
|
351
|
+
### v0.x · 脚本外挂时代
|
|
302
352
|
|
|
303
353
|
- 前身为 [kohaku-heartbeat](https://github.com/Kanadego/kohaku-heartbeat)——v1 脚本外挂形态,跑通"定时唤醒 + 采集 + 分寸表达"闭环后推倒重来为本插件形态。
|
|
304
|
-
</details>
|
|
305
354
|
|
|
306
355
|
</details>
|
|
307
356
|
|
package/cordis.patch.yml
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
- id: heartbeat
|
|
14
14
|
name: '@kanadego/dsh-heartbeat'
|
|
15
15
|
|
|
16
|
-
# ── Agent preset heartbeat(0.1.7 声明式预设,v1.7.
|
|
16
|
+
# ── Agent preset heartbeat(0.1.7 声明式预设,v1.7.1)─────────────────
|
|
17
17
|
# 0.1.7 起 agentPresets 名册不再挂载文件系统用户根(~/.dsh/.agent-presets),
|
|
18
18
|
# 预设改为组合里的 @deepseek-ai/dsh-agent-preset 声明行(与官方 preset-standard
|
|
19
19
|
# 同款机制)。内容 = assets/presets/heartbeat/agent.cordis.yml 的移植:
|
package/dist/index.js
CHANGED
|
@@ -64,7 +64,7 @@ import {
|
|
|
64
64
|
removeBinding
|
|
65
65
|
} from "./chunk-J6ZTRFFW.js";
|
|
66
66
|
|
|
67
|
-
// node_modules
|
|
67
|
+
// node_modules/@deepseek-ai/cosmokit/lib/index.js
|
|
68
68
|
function isNullable(value) {
|
|
69
69
|
return value === null || value === void 0;
|
|
70
70
|
}
|
|
@@ -83,6 +83,32 @@ function pick(source, keys, forced) {
|
|
|
83
83
|
for (const key of keys) if (forced || source[key] !== void 0) result[key] = source[key];
|
|
84
84
|
return result;
|
|
85
85
|
}
|
|
86
|
+
var write = /* @__PURE__ */ Symbol.for("cosmokit.volatile.write");
|
|
87
|
+
function snapshot(value, ancestors = /* @__PURE__ */ new Set()) {
|
|
88
|
+
if (typeof value === "function") throw new TypeError("volatile config cannot contain functions");
|
|
89
|
+
if (value === null || typeof value !== "object") return value;
|
|
90
|
+
if (ancestors.has(value)) throw new TypeError("volatile config cannot contain cycles");
|
|
91
|
+
ancestors.add(value);
|
|
92
|
+
try {
|
|
93
|
+
if (Array.isArray(value)) return Object.freeze(value.map((item) => snapshot(item, ancestors)));
|
|
94
|
+
if (Object.getPrototypeOf(value) !== Object.prototype && Object.getPrototypeOf(value) !== null) throw new TypeError("volatile config objects must be plain objects or arrays");
|
|
95
|
+
return Object.freeze(Object.fromEntries(Object.entries(value).map(([key, item]) => [key, snapshot(item, ancestors)])));
|
|
96
|
+
} finally {
|
|
97
|
+
ancestors.delete(value);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
function createVolatile(value) {
|
|
101
|
+
let current = snapshot(value);
|
|
102
|
+
return Object.freeze({
|
|
103
|
+
get: () => current,
|
|
104
|
+
[write]: (value2) => {
|
|
105
|
+
current = value2;
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
function isVolatile(value) {
|
|
110
|
+
return typeof value === "object" && value !== null && write in value;
|
|
111
|
+
}
|
|
86
112
|
function is(type, value) {
|
|
87
113
|
if (arguments.length === 1) return (value2) => is(type, value2);
|
|
88
114
|
return type in globalThis && value instanceof globalThis[type] || Object.prototype.toString.call(value).slice(8, -1) === type;
|
|
@@ -161,24 +187,37 @@ function clone(source, refs = /* @__PURE__ */ new Map()) {
|
|
|
161
187
|
return result;
|
|
162
188
|
}
|
|
163
189
|
function deepEqual(a, b, strict) {
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
190
|
+
const ancestors = /* @__PURE__ */ new Set();
|
|
191
|
+
function compare(a2, b2) {
|
|
192
|
+
if (a2 === b2) return true;
|
|
193
|
+
if (isVolatile(a2) || isVolatile(b2)) return isVolatile(a2) && isVolatile(b2);
|
|
194
|
+
if (!strict && isNullable(a2) && isNullable(b2)) return true;
|
|
195
|
+
if (typeof a2 !== typeof b2 || typeof a2 !== "object" || !a2 || !b2) return false;
|
|
196
|
+
if (ancestors.has(a2)) return false;
|
|
197
|
+
function check(test, then) {
|
|
198
|
+
return test(a2) ? test(b2) ? then(a2, b2) : false : test(b2) ? false : void 0;
|
|
199
|
+
}
|
|
200
|
+
ancestors.add(a2);
|
|
201
|
+
try {
|
|
202
|
+
return check(Array.isArray, (a3, b3) => {
|
|
203
|
+
if (a3.length !== b3.length) return false;
|
|
204
|
+
for (let index = 0; index < a3.length; index++) if (!compare(a3[index], b3[index])) return false;
|
|
205
|
+
return true;
|
|
206
|
+
}) ?? check(is("Date"), (a3, b3) => a3.valueOf() === b3.valueOf()) ?? check(is("URL"), (a3, b3) => a3.href === b3.href) ?? check(is("RegExp"), (a3, b3) => a3.source === b3.source && a3.flags === b3.flags) ?? check(isArrayBufferLike, (a3, b3) => {
|
|
207
|
+
if (a3.byteLength !== b3.byteLength) return false;
|
|
208
|
+
const viewA = new Uint8Array(a3);
|
|
209
|
+
const viewB = new Uint8Array(b3);
|
|
210
|
+
for (let i = 0; i < viewA.length; i++) if (viewA[i] !== viewB[i]) return false;
|
|
211
|
+
return true;
|
|
212
|
+
}) ?? ((!strict || [a2, b2].every((value) => Object.getPrototypeOf(value) === Object.prototype || Object.getPrototypeOf(value) === null)) && Object.keys({
|
|
213
|
+
...a2,
|
|
214
|
+
...b2
|
|
215
|
+
}).every((key) => compare(a2[key], b2[key])));
|
|
216
|
+
} finally {
|
|
217
|
+
ancestors.delete(a2);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
return compare(a, b);
|
|
182
221
|
}
|
|
183
222
|
var Time;
|
|
184
223
|
(function(Time2) {
|
|
@@ -250,7 +289,7 @@ var Time;
|
|
|
250
289
|
Time2.template = template;
|
|
251
290
|
})(Time || (Time = {}));
|
|
252
291
|
|
|
253
|
-
// node_modules
|
|
292
|
+
// node_modules/@deepseek-ai/schemastery/lib/index.mjs
|
|
254
293
|
var kSchema = /* @__PURE__ */ Symbol.for("schemastery");
|
|
255
294
|
var kValidationError = /* @__PURE__ */ Symbol.for("ValidationError");
|
|
256
295
|
globalThis.__schemastery_index__ ??= 0;
|
|
@@ -427,6 +466,7 @@ Schema.prototype.pattern = function pattern(regexp) {
|
|
|
427
466
|
return schema;
|
|
428
467
|
};
|
|
429
468
|
Schema.prototype.simplify = function simplify(value) {
|
|
469
|
+
if (isVolatile(value)) value = value.get();
|
|
430
470
|
if (deepEqual(value, this.meta.default, this.type === "dict")) return null;
|
|
431
471
|
if (isNullable(value)) return value;
|
|
432
472
|
if (this.type === "object" || this.type === "dict") {
|
|
@@ -484,12 +524,49 @@ for (const key of [
|
|
|
484
524
|
};
|
|
485
525
|
return schema;
|
|
486
526
|
} });
|
|
527
|
+
Schema.prototype.volatile = function volatile() {
|
|
528
|
+
if (this.meta.volatile) throw new TypeError("volatile schema is already wrapped");
|
|
529
|
+
return this.extra("volatile", true);
|
|
530
|
+
};
|
|
487
531
|
var resolvers = {};
|
|
532
|
+
var checkedVolatile = /* @__PURE__ */ Symbol("checked-volatile-schema");
|
|
533
|
+
function validateVolatileSchema(schema, path13 = [], blocked = false, seen = /* @__PURE__ */ new Map()) {
|
|
534
|
+
const states = seen.get(schema) ?? /* @__PURE__ */ new Set();
|
|
535
|
+
if (states.has(blocked)) return;
|
|
536
|
+
states.add(blocked);
|
|
537
|
+
seen.set(schema, states);
|
|
538
|
+
if (schema.meta?.volatile && blocked) throw new ValidationError("volatile fields require a fixed object path without an enclosing volatile field", { path: path13 });
|
|
539
|
+
const nested = blocked || !!schema.meta?.volatile;
|
|
540
|
+
if (schema.dict) for (const [key, child] of Object.entries(schema.dict)) validateVolatileSchema(child, [...path13, key], nested, seen);
|
|
541
|
+
if (schema.sKey) validateVolatileSchema(schema.sKey, [...path13, "<key>"], true, seen);
|
|
542
|
+
if (schema.inner && (schema.type !== "lazy" || schema.inner[kSchema])) validateVolatileSchema(schema.inner, [...path13, "*"], true, seen);
|
|
543
|
+
if (schema.list) for (let index = 0; index < schema.list.length; index++) validateVolatileSchema(schema.list[index], [...path13, String(index)], true, seen);
|
|
544
|
+
}
|
|
488
545
|
Schema.extend = function extend(type, resolve2) {
|
|
489
546
|
resolvers[type] = resolve2;
|
|
490
547
|
};
|
|
491
548
|
Schema.resolve = function resolve(data, schema, options = {}, strict = false) {
|
|
492
549
|
if (!schema) return [data];
|
|
550
|
+
if (!options[checkedVolatile]) {
|
|
551
|
+
validateVolatileSchema(schema, options.path);
|
|
552
|
+
options = {
|
|
553
|
+
...options,
|
|
554
|
+
[checkedVolatile]: true
|
|
555
|
+
};
|
|
556
|
+
}
|
|
557
|
+
if (schema.meta?.volatile) {
|
|
558
|
+
const inner = Schema(schema);
|
|
559
|
+
inner.meta = {
|
|
560
|
+
...schema.meta,
|
|
561
|
+
volatile: false
|
|
562
|
+
};
|
|
563
|
+
const [value, adapted] = Schema.resolve(data, inner, options, strict);
|
|
564
|
+
try {
|
|
565
|
+
return [createVolatile(value), adapted];
|
|
566
|
+
} catch (error) {
|
|
567
|
+
throw new ValidationError(error instanceof Error ? error.message : String(error), options);
|
|
568
|
+
}
|
|
569
|
+
}
|
|
493
570
|
if (options.ignore?.(data, schema)) return [data];
|
|
494
571
|
if (isNullable(data) && schema.type !== "lazy") {
|
|
495
572
|
if (schema.meta.required) throw new ValidationError(`missing required value`, options);
|
|
@@ -597,6 +674,7 @@ Schema.extend("lazy", (data, schema, options, strict) => {
|
|
|
597
674
|
...schema.meta,
|
|
598
675
|
...schema.inner.meta
|
|
599
676
|
};
|
|
677
|
+
validateVolatileSchema(schema.inner, options.path, true);
|
|
600
678
|
}
|
|
601
679
|
return Schema.resolve(data, schema.inner, options, strict);
|
|
602
680
|
});
|
|
@@ -696,7 +774,7 @@ function property(data, key, schema, options) {
|
|
|
696
774
|
} catch (e) {
|
|
697
775
|
if (!options?.autofix) throw e;
|
|
698
776
|
delete data[key];
|
|
699
|
-
return schema.meta.default;
|
|
777
|
+
return schema.meta.volatile ? createVolatile(schema.meta.default) : schema.meta.default;
|
|
700
778
|
}
|
|
701
779
|
}
|
|
702
780
|
Schema.extend("array", (data, { inner, meta }, options) => {
|
|
@@ -1026,7 +1104,7 @@ function collectPulse(guard, paths, rules, fgProcess = null, now = /* @__PURE__
|
|
|
1026
1104
|
const idle = probeIdle(guard, paths);
|
|
1027
1105
|
const windowClass = classifyProcess(fgProcess, rules);
|
|
1028
1106
|
const t = timeContext(now);
|
|
1029
|
-
const
|
|
1107
|
+
const snapshot2 = {
|
|
1030
1108
|
takenAt: now.toISOString(),
|
|
1031
1109
|
idleSeconds: idle,
|
|
1032
1110
|
presence: presenceOf(idle, windowClass),
|
|
@@ -1037,24 +1115,24 @@ function collectPulse(guard, paths, rules, fgProcess = null, now = /* @__PURE__
|
|
|
1037
1115
|
festival: t.festival
|
|
1038
1116
|
};
|
|
1039
1117
|
try {
|
|
1040
|
-
fs3.writeFileSync(path3.join(paths.dataDir, "envpulse.json"), JSON.stringify(
|
|
1118
|
+
fs3.writeFileSync(path3.join(paths.dataDir, "envpulse.json"), JSON.stringify(snapshot2, null, 1), "utf8");
|
|
1041
1119
|
} catch {
|
|
1042
1120
|
}
|
|
1043
|
-
writePulseStream(paths,
|
|
1044
|
-
return
|
|
1121
|
+
writePulseStream(paths, snapshot2);
|
|
1122
|
+
return snapshot2;
|
|
1045
1123
|
}
|
|
1046
|
-
function writePulseStream(paths,
|
|
1124
|
+
function writePulseStream(paths, snapshot2) {
|
|
1047
1125
|
try {
|
|
1048
1126
|
appendAuditLine(path3.join(paths.logsDir, "envpulse.jsonl"), {
|
|
1049
1127
|
event: "pulse",
|
|
1050
|
-
takenAt:
|
|
1051
|
-
idleSeconds:
|
|
1052
|
-
presence:
|
|
1053
|
-
windowClass:
|
|
1054
|
-
daypart:
|
|
1055
|
-
weekday:
|
|
1056
|
-
isWeekend:
|
|
1057
|
-
festival:
|
|
1128
|
+
takenAt: snapshot2.takenAt,
|
|
1129
|
+
idleSeconds: snapshot2.idleSeconds,
|
|
1130
|
+
presence: snapshot2.presence,
|
|
1131
|
+
windowClass: snapshot2.windowClass,
|
|
1132
|
+
daypart: snapshot2.daypart,
|
|
1133
|
+
weekday: snapshot2.weekday,
|
|
1134
|
+
isWeekend: snapshot2.isWeekend,
|
|
1135
|
+
festival: snapshot2.festival
|
|
1058
1136
|
});
|
|
1059
1137
|
} catch {
|
|
1060
1138
|
}
|
|
@@ -1799,8 +1877,8 @@ function sessionEvents(session) {
|
|
|
1799
1877
|
if (!session) return [];
|
|
1800
1878
|
if (typeof session.snapshotEvents === "function") {
|
|
1801
1879
|
try {
|
|
1802
|
-
const
|
|
1803
|
-
if (Array.isArray(
|
|
1880
|
+
const snapshot2 = session.snapshotEvents();
|
|
1881
|
+
if (Array.isArray(snapshot2)) return snapshot2;
|
|
1804
1882
|
} catch {
|
|
1805
1883
|
}
|
|
1806
1884
|
}
|
|
@@ -1815,6 +1893,7 @@ var IDLE_WAIT_TIMEOUT_MS = 24e4;
|
|
|
1815
1893
|
var EXPRESSION_IDLE_WAIT_MS = 6e5;
|
|
1816
1894
|
var agentPromise = null;
|
|
1817
1895
|
var beating = false;
|
|
1896
|
+
var beatCancel;
|
|
1818
1897
|
var lastBeat = null;
|
|
1819
1898
|
function getLastBeat() {
|
|
1820
1899
|
return lastBeat;
|
|
@@ -2468,6 +2547,14 @@ async function beat(deps) {
|
|
|
2468
2547
|
appendAuditLine(paths.logsDir + "/heartbeat.jsonl", { event: "beat_start" });
|
|
2469
2548
|
const beatStart = new Date(now).toISOString();
|
|
2470
2549
|
const agent = await ensureAgent(deps);
|
|
2550
|
+
if (agent) {
|
|
2551
|
+
beatCancel = () => {
|
|
2552
|
+
try {
|
|
2553
|
+
agent.cancel?.();
|
|
2554
|
+
} catch {
|
|
2555
|
+
}
|
|
2556
|
+
};
|
|
2557
|
+
}
|
|
2471
2558
|
let wandered = false;
|
|
2472
2559
|
if (agent) {
|
|
2473
2560
|
deferredRetries = 0;
|
|
@@ -2506,6 +2593,7 @@ async function beat(deps) {
|
|
|
2506
2593
|
}
|
|
2507
2594
|
} finally {
|
|
2508
2595
|
beating = false;
|
|
2596
|
+
beatCancel = void 0;
|
|
2509
2597
|
}
|
|
2510
2598
|
}
|
|
2511
2599
|
function startOrchestrator(deps) {
|
|
@@ -2541,7 +2629,12 @@ function startOrchestrator(deps) {
|
|
|
2541
2629
|
return () => {
|
|
2542
2630
|
if (timer) clearInterval(timer);
|
|
2543
2631
|
if (first) clearTimeout(first);
|
|
2544
|
-
|
|
2632
|
+
const cancelledInFlight = beating && beatCancel !== void 0;
|
|
2633
|
+
try {
|
|
2634
|
+
beatCancel?.();
|
|
2635
|
+
} catch {
|
|
2636
|
+
}
|
|
2637
|
+
appendAuditLine(deps.paths.logsDir + "/heartbeat.jsonl", { event: "orchestrator_disposed", beatCancelled: cancelledInFlight });
|
|
2545
2638
|
deps.ctx.logger.info("heartbeat: orchestrator timer disposed");
|
|
2546
2639
|
};
|
|
2547
2640
|
}, "heartbeat: timer");
|