ppxans-harness 2.4.0 → 3.2.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/LICENSE +201 -201
- package/README.md +218 -265
- package/bin/ppx-channels.js +2 -2
- package/bin/ppx-serve.js +5 -5
- package/bin/ppx-setup.js +124 -0
- package/bin/ppx-web.js +140 -0
- package/bin/ppx.js +2 -2
- package/config/identity.md +6 -6
- package/config/ishiki.md +16 -16
- package/config/ppx.json +15 -151
- package/config/ppx.json.example +143 -0
- package/package.json +17 -10
- package/skills/.usage.json +6 -0
- package/skills/agent-professional-training/SKILL.md +94 -0
- package/skills/brainstorm/SKILL.md +24 -0
- package/skills/cupid-lover-comms/SKILL.md +37 -0
- package/skills/debug/SKILL.md +26 -0
- package/skills/plan/SKILL.md +25 -0
- package/skills/ponytail/SKILL.md +25 -0
- package/skills/ppx-memory/SKILL.md +91 -0
- package/skills/ppx-memory/scripts/cli.js +192 -0
- package/skills/ppx-memory/scripts/experience.js +133 -0
- package/skills/ppx-memory/scripts/fact-store.js +842 -0
- package/skills/ppx-memory/scripts/l0.js +52 -0
- package/skills/ppx-memory/scripts/l2.js +146 -0
- package/skills/ppx-memory/scripts/l3.js +112 -0
- package/skills/ppx-memory/scripts/memory-ticker.js +238 -0
- package/skills/ppx-memory/scripts/pii.js +42 -0
- package/skills/ppx-memory/scripts/schema.js +80 -0
- package/skills/ppx-memory/scripts/session.js +398 -0
- package/skills/ppx-memory/scripts/similarity.js +43 -0
- package/skills/ppx-memory/scripts/store.js +116 -0
- package/skills/ppx-memory/scripts/wal.js +38 -0
- package/skills/ppx-selfheal/SKILL.md +24 -0
- package/skills/ppx-selfheal/scripts/cli.js +80 -0
- package/skills/ppx-selfheal/scripts/healer.js +184 -0
- package/skills/ppx-selfheal/scripts/logger.js +17 -0
- package/skills/ppx-selfheal/scripts/store.js +116 -0
- package/skills/prompt-depth-kit/SKILL.md +28 -0
- package/skills/session-naming/SKILL.md +36 -0
- package/skills/verify/SKILL.md +25 -0
- package/src/agent/index.js +1340 -717
- package/src/agent/prompts.js +47 -3
- package/src/aml-server.js +197 -151
- package/src/ans/eviction.js +123 -143
- package/src/ans/guard.js +159 -120
- package/src/ans/lifecycle.js +96 -93
- package/src/ans/proactive.js +112 -129
- package/src/ans/reward.js +95 -111
- package/src/ans/values.js +15 -15
- package/src/audit/audit-chain.js +43 -7
- package/src/audit/verifier.js +157 -120
- package/src/bus/circuit-breaker.js +9 -1
- package/src/bus/runtime-bus.js +107 -93
- package/src/channels/base.js +57 -34
- package/src/channels/feishu.js +118 -126
- package/src/channels/http.js +1090 -592
- package/src/channels/index.js +111 -110
- package/src/channels/log.js +29 -29
- package/src/channels/wechat-crypto.js +73 -74
- package/src/channels/wechat.js +191 -197
- package/src/channels/workspace.js +94 -0
- package/src/channels-cli.js +126 -124
- package/src/cli.js +129 -120
- package/src/commands/index.js +142 -0
- package/src/config/channels.js +137 -170
- package/src/config/index.js +277 -224
- package/src/config/placeholder.js +31 -0
- package/src/config/providers.js +148 -188
- package/src/config/settings.js +156 -182
- package/src/core/policy.js +69 -18
- package/src/core/trace.js +8 -6
- package/src/edit/editblock.js +266 -0
- package/src/edit/snapshot.js +67 -0
- package/src/evidence/index.js +153 -0
- package/src/evolve/playbook.js +9 -10
- package/src/hooks/index.js +113 -0
- package/src/llm/client.js +188 -446
- package/src/llm/dsml.js +74 -74
- package/src/llm/embedder.js +41 -35
- package/src/llm/fence.js +52 -105
- package/src/llm/index.js +4 -4
- package/src/llm/local-embedder.js +94 -0
- package/src/llm/presets.js +113 -0
- package/src/llm/pricing.js +93 -0
- package/src/llm/retry.js +73 -73
- package/src/llm/router.js +92 -97
- package/src/mcp/admin.js +326 -0
- package/src/mcp/client.js +487 -375
- package/src/mcp/http.js +203 -0
- package/src/mcp/index.js +116 -116
- package/src/mcp/server.js +392 -0
- package/src/mcp/tasks.js +133 -0
- package/src/memory/asset-hub.js +6 -11
- package/src/memory/canvas.js +2 -5
- package/src/memory/compaction.js +28 -28
- package/src/memory/experience.js +133 -122
- package/src/memory/fact-store.js +914 -698
- package/src/memory/failure-episode.js +20 -11
- package/src/memory/fork.js +17 -8
- package/src/memory/index.js +8 -6
- package/src/memory/l0.js +53 -52
- package/src/memory/l2.js +145 -130
- package/src/memory/l3.js +111 -111
- package/src/memory/legion-board.js +71 -0
- package/src/memory/memory-ticker.js +239 -240
- package/src/memory/session.js +398 -397
- package/src/memory/sqlite-store.js +581 -0
- package/src/mode/blackboard.js +49 -49
- package/src/mode/graph.js +42 -41
- package/src/mode/index.js +64 -64
- package/src/mode/legion.js +54 -51
- package/src/mode/plan-exec.js +50 -50
- package/src/mode/router.js +28 -40
- package/src/orchestrator/agent-worker.js +69 -69
- package/src/orchestrator/dag.js +90 -83
- package/src/orchestrator/experts.js +76 -0
- package/src/orchestrator/index.js +1 -1
- package/src/orchestrator/legion.js +179 -187
- package/src/orchestrator/supervisor.js +6 -8
- package/src/permissions/index.js +378 -0
- package/src/persona/index.js +28 -29
- package/src/plugin/builtin.js +313 -212
- package/src/plugin/context.js +80 -79
- package/src/plugin/index.js +62 -62
- package/src/plugin/v3.js +73 -0
- package/src/protocol/index.js +148 -0
- package/src/repomap/index.js +309 -0
- package/src/review/index.js +393 -0
- package/src/seam/registry.js +3 -0
- package/src/seam/shell.js +55 -55
- package/src/security/injection.js +79 -0
- package/src/selfheal/evolve.js +67 -67
- package/src/selfheal/healer.js +184 -167
- package/src/selfheal/run.js +9 -9
- package/src/server.js +63 -60
- package/src/services/diagnose.js +180 -0
- package/src/services/learning-service.js +9 -0
- package/src/services/memory-health.js +34 -6
- package/src/services/memory-service.js +42 -9
- package/src/services/triage.js +138 -0
- package/src/session/parts.js +76 -0
- package/src/session/projection.js +73 -0
- package/src/session/rollout.js +54 -0
- package/src/session/turn.js +137 -0
- package/src/skills/lint.js +72 -0
- package/src/skills/loader.js +231 -150
- package/src/skills/search.js +58 -0
- package/src/skills/verify.js +95 -100
- package/src/tools/advanced.js +388 -352
- package/src/tools/builtin.js +384 -297
- package/src/tools/catalog.js +283 -159
- package/src/tools/command-guard.js +112 -112
- package/src/tools/custom.js +47 -47
- package/src/tools/delegate.js +383 -297
- package/src/tools/document.js +254 -253
- package/src/tools/git.js +151 -0
- package/src/tools/governance.js +47 -20
- package/src/tools/index.js +16 -11
- package/src/tools/methods.js +178 -178
- package/src/tools/ocr.js +59 -59
- package/src/tools/sandbox-worker.js +40 -0
- package/src/tools/sandbox.js +92 -0
- package/src/tools/seam.js +162 -125
- package/src/tools/selfmod.js +196 -176
- package/src/tools/v3.js +225 -0
- package/src/tools/vad.js +176 -0
- package/src/tools/voice.js +238 -0
- package/src/utils/async.js +14 -0
- package/src/utils/config-file.js +53 -0
- package/src/utils/crashguard.js +88 -0
- package/src/utils/http.js +53 -0
- package/src/utils/id.js +8 -0
- package/src/utils/json-state.js +33 -0
- package/src/utils/logger.js +17 -17
- package/src/utils/ndjson.js +25 -0
- package/src/utils/pii.js +42 -42
- package/src/utils/rate-limit.js +50 -0
- package/src/utils/schema.js +80 -0
- package/src/utils/similarity.js +43 -0
- package/src/utils/store.js +170 -108
- package/src/utils/text.js +15 -15
- package/src/utils/trace.js +153 -153
- package/src/utils/wal.js +39 -0
- package/src/utils/winutf8.js +16 -15
- package/src/wiki/index.js +170 -0
package/src/ans/lifecycle.js
CHANGED
|
@@ -1,93 +1,96 @@
|
|
|
1
|
-
// src/ans/lifecycle.js - 生命周期状态机 (ANS)
|
|
2
|
-
// 独立可更换模块: agent 只持有 Lifecycle 实例, 需要自定义阶段逻辑时替换此模块即可
|
|
3
|
-
// 阶段: born → growing(首次对话) → mature(10 次对话); evolving(进化)/ reproducing(繁衍) 为累计计数
|
|
4
|
-
// v1.0.7 持久化: 传入 file 时状态落盘 JSON, 跨进程/重启不归零 (P1)
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
this.
|
|
16
|
-
this.
|
|
17
|
-
this.
|
|
18
|
-
this.
|
|
19
|
-
this.
|
|
20
|
-
this.
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
if (
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
this.
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
this.
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
1
|
+
// src/ans/lifecycle.js - 生命周期状态机 (ANS)
|
|
2
|
+
// 独立可更换模块: agent 只持有 Lifecycle 实例, 需要自定义阶段逻辑时替换此模块即可
|
|
3
|
+
// 阶段: born → growing(首次对话) → mature(10 次对话); evolving(进化)/ reproducing(繁衍) 为累计计数
|
|
4
|
+
// v1.0.7 持久化: 传入 file 时状态落盘 JSON, 跨进程/重启不归零 (P1)
|
|
5
|
+
import { readJson, writeJson } from "../utils/store.js";
|
|
6
|
+
import { info } from "../utils/logger.js";
|
|
7
|
+
|
|
8
|
+
const MATURE_CHATS = 10; // 对话达到该次数 → mature
|
|
9
|
+
const LOG_LIMIT = 50; // 阶段日志上限 (环形)
|
|
10
|
+
|
|
11
|
+
export class Lifecycle {
|
|
12
|
+
constructor({ file = null } = {}) {
|
|
13
|
+
this.file = file || null;
|
|
14
|
+
this.stage = "born";
|
|
15
|
+
this.bornAt = Date.now();
|
|
16
|
+
this.chats = 0;
|
|
17
|
+
this.evolved = 0;
|
|
18
|
+
this.reproduced = 0;
|
|
19
|
+
this.log = [];
|
|
20
|
+
this._load();
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// 从磁盘恢复状态 (跨进程/重启), 文件缺失或损坏时静默用初始值
|
|
24
|
+
_load() {
|
|
25
|
+
if (!this.file) return;
|
|
26
|
+
const s = readJson(this.file, null); // 损坏文件静默忽略, 用初始状态
|
|
27
|
+
if (s && typeof s.stage === "string" && s.stage) {
|
|
28
|
+
this.stage = s.stage;
|
|
29
|
+
this.bornAt = s.bornAt || Date.now();
|
|
30
|
+
this.chats = Number(s.chats) || 0;
|
|
31
|
+
this.evolved = Number(s.evolved) || 0;
|
|
32
|
+
this.reproduced = Number(s.reproduced) || 0;
|
|
33
|
+
this.log = Array.isArray(s.log) ? s.log.slice(-LOG_LIMIT) : [];
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
_save() {
|
|
38
|
+
if (!this.file) return;
|
|
39
|
+
try {
|
|
40
|
+
writeJson(this.file, {
|
|
41
|
+
stage: this.stage,
|
|
42
|
+
bornAt: this.bornAt,
|
|
43
|
+
chats: this.chats,
|
|
44
|
+
evolved: this.evolved,
|
|
45
|
+
reproduced: this.reproduced,
|
|
46
|
+
log: this.log.slice(-LOG_LIMIT),
|
|
47
|
+
});
|
|
48
|
+
} catch { /* 状态落盘失败不影响主流程 */ }
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// 每次对话推进: born → growing → mature (计数由调用方保证每次对话调一次)
|
|
52
|
+
tick() {
|
|
53
|
+
this.chats += 1;
|
|
54
|
+
if (this.stage === "born") this.to("growing", "首次对话");
|
|
55
|
+
else if (this.stage === "growing" && this.chats >= MATURE_CHATS) this.to("mature", `完成 ${this.chats} 次对话`);
|
|
56
|
+
this._save();
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// 阶段转换 (含日志), 供进化/繁衍等外部事件调用
|
|
60
|
+
to(stage, note) {
|
|
61
|
+
this.stage = stage;
|
|
62
|
+
this.log.push({ stage, note, ts: Date.now() });
|
|
63
|
+
if (this.log.length > LOG_LIMIT) this.log.shift();
|
|
64
|
+
info(`[lifecycle] ${stage}: ${note}`);
|
|
65
|
+
this._save();
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// 进化计数 (refine/分享经验), 内部落盘
|
|
69
|
+
// 2026-10-03 深度优化: evolving 从"纯计数器"升为真阶段 —— 首次进化且已 mature 时推进 stage,
|
|
70
|
+
// 修复审计指出的 "阶段名存在但永远不可达" (计数器≠阶段, 状态机撒谎)。
|
|
71
|
+
evolve(n = 1) {
|
|
72
|
+
this.evolved += n;
|
|
73
|
+
if (this.stage === "mature") this.to("evolving", `首次进化 (累计 ${this.evolved} 次)`);
|
|
74
|
+
else this._save();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// 繁衍计数 (spawn_agent), 内部落盘; 首次繁衍推进到 reproducing 阶段
|
|
78
|
+
reproduce(n = 1) {
|
|
79
|
+
this.reproduced += n;
|
|
80
|
+
if (this.stage === "mature" || this.stage === "evolving") this.to("reproducing", `首次繁衍 (累计 ${this.reproduced} 次)`);
|
|
81
|
+
else this._save();
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// 人类可读摘要 (可观测)
|
|
85
|
+
status() {
|
|
86
|
+
return {
|
|
87
|
+
stage: this.stage,
|
|
88
|
+
bornAt: new Date(this.bornAt).toISOString(),
|
|
89
|
+
ageMs: Date.now() - this.bornAt,
|
|
90
|
+
chats: this.chats,
|
|
91
|
+
evolved: this.evolved,
|
|
92
|
+
reproduced: this.reproduced,
|
|
93
|
+
recent: this.log.slice(-5),
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
}
|
package/src/ans/proactive.js
CHANGED
|
@@ -1,129 +1,112 @@
|
|
|
1
|
-
// src/ans/proactive.js - 自主任务生成 (ANS)
|
|
2
|
-
// 扫描 L1 事实(FactStore.list())里的待办/偏好信号 → 结构化提醒 payload
|
|
3
|
-
//
|
|
4
|
-
// 输出契约 (ticker 回调 / 通道消费方):
|
|
5
|
-
// payload = { ts: number, items: Array<{content, importance, source, id}>, text: string }
|
|
6
|
-
// - text: 可直接投递的一句话文本 (LLM 语义提炼或启发式拼接)
|
|
7
|
-
// - items: 结构化条目, 供通道方自行排版/去重/分级
|
|
8
|
-
// - 无待办信号或生成失败 → null (不打扰)
|
|
9
|
-
// 投递保证: 非 null 才回调; 回调异常被捕获, 不影响定时器
|
|
10
|
-
//
|
|
11
|
-
// v1.0.7 去重 + 完成跟踪 (P1):
|
|
12
|
-
// - 提醒状态存 data/memory/proactive.json: { [factId]: { lastRemindedAt, done } }
|
|
13
|
-
// - 同一待办在窗口内 (默认 24h) 不重复提醒; 标记完成的待办不再提醒
|
|
14
|
-
import
|
|
15
|
-
import
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
//
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
const
|
|
27
|
-
if (
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
return
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
const
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
], { timeoutMs: 10000, retryMax: 0 });
|
|
114
|
-
text = String(r?.content || "").trim();
|
|
115
|
-
if (!text) return null;
|
|
116
|
-
}
|
|
117
|
-
// 记录已提醒时间戳 (窗口去重)
|
|
118
|
-
const state = loadState(agent);
|
|
119
|
-
for (const it of items) {
|
|
120
|
-
if (!it.id) continue;
|
|
121
|
-
state[it.id] = { ...(state[it.id] || {}), lastRemindedAt: Date.now() };
|
|
122
|
-
}
|
|
123
|
-
saveState(agent, state);
|
|
124
|
-
return { ts: Date.now(), items, text };
|
|
125
|
-
} catch (e) {
|
|
126
|
-
info(`[proactive] 生成失败: ${e.message}`);
|
|
127
|
-
return null;
|
|
128
|
-
}
|
|
129
|
-
}
|
|
1
|
+
// src/ans/proactive.js - 自主任务生成 (ANS)
|
|
2
|
+
// 扫描 L1 事实(FactStore.list())里的待办/偏好信号 → 结构化提醒 payload
|
|
3
|
+
//
|
|
4
|
+
// 输出契约 (ticker 回调 / 通道消费方):
|
|
5
|
+
// payload = { ts: number, items: Array<{content, importance, source, id}>, text: string }
|
|
6
|
+
// - text: 可直接投递的一句话文本 (LLM 语义提炼或启发式拼接)
|
|
7
|
+
// - items: 结构化条目, 供通道方自行排版/去重/分级
|
|
8
|
+
// - 无待办信号或生成失败 → null (不打扰)
|
|
9
|
+
// 投递保证: 非 null 才回调; 回调异常被捕获, 不影响定时器
|
|
10
|
+
//
|
|
11
|
+
// v1.0.7 去重 + 完成跟踪 (P1):
|
|
12
|
+
// - 提醒状态存 data/memory/proactive.json: { [factId]: { lastRemindedAt, done } }
|
|
13
|
+
// - 同一待办在窗口内 (默认 24h) 不重复提醒; 标记完成的待办不再提醒
|
|
14
|
+
import { loadAgentState, saveAgentState } from "../utils/json-state.js";
|
|
15
|
+
import { info } from "../utils/logger.js";
|
|
16
|
+
|
|
17
|
+
// 待办/偏好信号词 (启发式)
|
|
18
|
+
const PENDING_RE = /待办|需要|记得|计划|还没|未完成|想|要|必须/;
|
|
19
|
+
// 重复提醒窗口 (默认 24h)
|
|
20
|
+
const DEFAULT_WINDOW_MS = 24 * 3600 * 1000;
|
|
21
|
+
|
|
22
|
+
// 带时效待办过期检测: 内容含明确过去时间 ("昨天/上周" 或已过的具体日期) → 不再提醒 (v1.0.7)
|
|
23
|
+
export function isExpired(content) {
|
|
24
|
+
const s = String(content || "");
|
|
25
|
+
if (/昨天|上周|上个月|上星期/.test(s)) return true;
|
|
26
|
+
const m = s.match(/(\d{4})[-年](\d{1,2})[-月](\d{1,2})日?/);
|
|
27
|
+
if (m) {
|
|
28
|
+
const d = new Date(+m[1], +m[2] - 1, +m[3]).getTime();
|
|
29
|
+
return !Number.isNaN(d) && d < Date.now();
|
|
30
|
+
}
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// 提醒状态读写 (实现收敛到 utils/json-state, 对外导出签名不变)
|
|
35
|
+
export function loadState(agent) {
|
|
36
|
+
return loadAgentState(agent, "proactive");
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function saveState(agent, state) {
|
|
40
|
+
return saveAgentState(agent, "proactive", state);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// 结构化扫描: 从 L1 事实里筛出待办信号 (契约的数据源)
|
|
44
|
+
// windowMs: 重复提醒窗口; done 的待办永不再提醒
|
|
45
|
+
export function pendingTasks(agent, { limit = 5, windowMs = DEFAULT_WINDOW_MS } = {}) {
|
|
46
|
+
if (!agent || !agent.facts) return [];
|
|
47
|
+
const state = loadState(agent);
|
|
48
|
+
const now = Date.now();
|
|
49
|
+
return agent.facts.list()
|
|
50
|
+
.filter((f) => f && PENDING_RE.test(String(f.content || "")))
|
|
51
|
+
.filter((f) => !isExpired(f.content)) // 已过期的不提醒
|
|
52
|
+
.filter((f) => {
|
|
53
|
+
const st = state[f.id];
|
|
54
|
+
if (!st) return true; // 从未提醒过
|
|
55
|
+
if (st.done) return false; // 已标记完成
|
|
56
|
+
if (st.lastRemindedAt && (now - st.lastRemindedAt) < windowMs) return false; // 窗口内不重复
|
|
57
|
+
return true;
|
|
58
|
+
})
|
|
59
|
+
.slice(0, limit)
|
|
60
|
+
.map((f) => ({
|
|
61
|
+
content: String(f.content || "").slice(0, 100),
|
|
62
|
+
importance: f.importance ?? 0,
|
|
63
|
+
source: f.source || "manual",
|
|
64
|
+
id: f.id || null,
|
|
65
|
+
}));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// 标记待办完成: 之后不再提醒
|
|
69
|
+
export function markTaskDone(agent, id) {
|
|
70
|
+
if (!id) return false;
|
|
71
|
+
const exists = agent && agent.facts && agent.facts.list().some((f) => f.id === id);
|
|
72
|
+
if (!exists) return false;
|
|
73
|
+
const state = loadState(agent);
|
|
74
|
+
state[id] = { ...(state[id] || {}), done: true, doneAt: Date.now() };
|
|
75
|
+
saveState(agent, state);
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// 生成主动提醒: 有 LLM 走语义提炼 (1-3 条简短提醒), 无 LLM 走启发式拼接
|
|
80
|
+
// 生成成功后记录 lastRemindedAt (去重); 失败/空不记录 (下次重试)
|
|
81
|
+
export async function suggestProactive(agent) {
|
|
82
|
+
try {
|
|
83
|
+
const items = pendingTasks(agent);
|
|
84
|
+
if (!items.length) return null;
|
|
85
|
+
const lines = items.map((t) => "- " + t.content);
|
|
86
|
+
// 模型不可用 (未配置/未运行) 时走启发式拼接, 不发起 LLM 等待 (v1.0.7 快速降级)
|
|
87
|
+
const llmReady = !agent.llm ? false : (agent._auxLlmReady ? await agent._auxLlmReady() : true);
|
|
88
|
+
let text;
|
|
89
|
+
if (!llmReady) {
|
|
90
|
+
text = "【主动提醒】你之前提到过:\n" + lines.join("\n");
|
|
91
|
+
} else {
|
|
92
|
+
// 短超时: 主动提醒是后台任务, LLM 不可用时快速降级启发式, 不阻塞 ticker
|
|
93
|
+
const r = await agent.llm.chat([
|
|
94
|
+
{ role: "system", content: "你是主动助手。基于用户历史提到的待办/偏好, 生成 1-3 条简短主动提醒 (每条 ≤30 字, 直接输出, 不要客套)。" },
|
|
95
|
+
{ role: "user", content: lines.join("\n") },
|
|
96
|
+
], { timeoutMs: 10000, retryMax: 0 });
|
|
97
|
+
text = String(r?.content || "").trim();
|
|
98
|
+
if (!text) return null;
|
|
99
|
+
}
|
|
100
|
+
// 记录已提醒时间戳 (窗口去重)
|
|
101
|
+
const state = loadState(agent);
|
|
102
|
+
for (const it of items) {
|
|
103
|
+
if (!it.id) continue;
|
|
104
|
+
state[it.id] = { ...(state[it.id] || {}), lastRemindedAt: Date.now() };
|
|
105
|
+
}
|
|
106
|
+
saveState(agent, state);
|
|
107
|
+
return { ts: Date.now(), items, text };
|
|
108
|
+
} catch (e) {
|
|
109
|
+
info(`[proactive] 生成失败: ${e.message}`);
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
}
|