@zooique/memora 4.0.0 → 5.1.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/CHANGELOG.md +235 -1743
- package/README.en.md +43 -36
- package/README.md +308 -303
- package/dist/agent/agent.d.ts +46 -82
- package/dist/agent/agent.d.ts.map +1 -1
- package/dist/agent/agent.js +153 -220
- package/dist/agent/agent.js.map +1 -1
- package/dist/agent/assembler.d.ts +3 -16
- package/dist/agent/assembler.d.ts.map +1 -1
- package/dist/agent/assembler.js +76 -47
- package/dist/agent/assembler.js.map +1 -1
- package/dist/agent/backgroundTasks.d.ts +8 -1
- package/dist/agent/backgroundTasks.d.ts.map +1 -1
- package/dist/agent/backgroundTasks.js +8 -1
- package/dist/agent/backgroundTasks.js.map +1 -1
- package/dist/agent/budget.d.ts +23 -5
- package/dist/agent/budget.d.ts.map +1 -1
- package/dist/agent/budget.js +8 -2
- package/dist/agent/budget.js.map +1 -1
- package/dist/agent/builtinToolHandlers.d.ts +50 -10
- package/dist/agent/builtinToolHandlers.d.ts.map +1 -1
- package/dist/agent/builtinToolHandlers.js +67 -20
- package/dist/agent/builtinToolHandlers.js.map +1 -1
- package/dist/agent/builtinTools.d.ts +16 -0
- package/dist/agent/builtinTools.d.ts.map +1 -1
- package/dist/agent/builtinTools.js +15 -4
- package/dist/agent/builtinTools.js.map +1 -1
- package/dist/agent/constants.d.ts +0 -6
- package/dist/agent/constants.d.ts.map +1 -1
- package/dist/agent/constants.js +7 -8
- package/dist/agent/constants.js.map +1 -1
- package/dist/agent/contextManager.d.ts +8 -0
- package/dist/agent/contextManager.d.ts.map +1 -1
- package/dist/agent/contextManager.js +45 -3
- package/dist/agent/contextManager.js.map +1 -1
- package/dist/agent/contextPreparer.d.ts +1 -1
- package/dist/agent/contextPreparer.d.ts.map +1 -1
- package/dist/agent/contextPreparer.js +15 -10
- package/dist/agent/contextPreparer.js.map +1 -1
- package/dist/agent/guardRail.d.ts +25 -10
- package/dist/agent/guardRail.d.ts.map +1 -1
- package/dist/agent/guardRail.js +154 -46
- package/dist/agent/guardRail.js.map +1 -1
- package/dist/agent/loop.d.ts +46 -13
- package/dist/agent/loop.d.ts.map +1 -1
- package/dist/agent/loop.js +145 -32
- package/dist/agent/loop.js.map +1 -1
- package/dist/agent/managers/archiveCoordinator.d.ts +15 -3
- package/dist/agent/managers/archiveCoordinator.d.ts.map +1 -1
- package/dist/agent/managers/archiveCoordinator.js +13 -7
- package/dist/agent/managers/archiveCoordinator.js.map +1 -1
- package/dist/agent/managers/chatLockManager.d.ts +3 -9
- package/dist/agent/managers/chatLockManager.d.ts.map +1 -1
- package/dist/agent/managers/chatLockManager.js +7 -25
- package/dist/agent/managers/chatLockManager.js.map +1 -1
- package/dist/agent/managers/llmCaller.d.ts.map +1 -1
- package/dist/agent/managers/llmCaller.js +27 -1
- package/dist/agent/managers/llmCaller.js.map +1 -1
- package/dist/agent/managers/memoryInspector.d.ts +12 -7
- package/dist/agent/managers/memoryInspector.d.ts.map +1 -1
- package/dist/agent/managers/memoryInspector.js +32 -19
- package/dist/agent/managers/memoryInspector.js.map +1 -1
- package/dist/agent/managers/roundSummaryGenerator.d.ts +3 -1
- package/dist/agent/managers/roundSummaryGenerator.d.ts.map +1 -1
- package/dist/agent/managers/roundSummaryGenerator.js +14 -4
- package/dist/agent/managers/roundSummaryGenerator.js.map +1 -1
- package/dist/agent/managers/sessionArchiver.d.ts +20 -6
- package/dist/agent/managers/sessionArchiver.d.ts.map +1 -1
- package/dist/agent/managers/sessionArchiver.js +19 -5
- package/dist/agent/managers/sessionArchiver.js.map +1 -1
- package/dist/agent/managers/sessionManager.d.ts +21 -88
- package/dist/agent/managers/sessionManager.d.ts.map +1 -1
- package/dist/agent/managers/sessionManager.js +77 -278
- package/dist/agent/managers/sessionManager.js.map +1 -1
- package/dist/agent/seed/orchestrator.d.ts.map +1 -1
- package/dist/agent/seed/orchestrator.js +3 -3
- package/dist/agent/seed/orchestrator.js.map +1 -1
- package/dist/agent/seed/prepare.d.ts.map +1 -1
- package/dist/agent/seed/prepare.js +9 -4
- package/dist/agent/seed/prepare.js.map +1 -1
- package/dist/agent/seed/types.d.ts +11 -4
- package/dist/agent/seed/types.d.ts.map +1 -1
- package/dist/agent/seed/types.js +2 -2
- package/dist/agent/toolExecutor.d.ts +69 -18
- package/dist/agent/toolExecutor.d.ts.map +1 -1
- package/dist/agent/toolExecutor.js +188 -28
- package/dist/agent/toolExecutor.js.map +1 -1
- package/dist/agent/toolResultCache.js +1 -1
- package/dist/agent/toolRunner.d.ts +4 -2
- package/dist/agent/toolRunner.d.ts.map +1 -1
- package/dist/agent/toolRunner.js +7 -4
- package/dist/agent/toolRunner.js.map +1 -1
- package/dist/agent/types.d.ts +8 -25
- package/dist/agent/types.d.ts.map +1 -1
- package/dist/index.d.ts +9 -20
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +35 -32
- package/dist/index.js.map +1 -1
- package/dist/logging/logger.d.ts +4 -1
- package/dist/logging/logger.d.ts.map +1 -1
- package/dist/logging/logger.js +5 -2
- package/dist/logging/logger.js.map +1 -1
- package/dist/memory/gcService.d.ts.map +1 -1
- package/dist/memory/gcService.js +3 -5
- package/dist/memory/gcService.js.map +1 -1
- package/dist/memory/inMemorySessionStore.d.ts +2 -2
- package/dist/memory/inMemorySessionStore.js +2 -2
- package/dist/memory/inMemoryStorage.d.ts +4 -3
- package/dist/memory/inMemoryStorage.d.ts.map +1 -1
- package/dist/memory/inMemoryStorage.js +2 -2
- package/dist/memory/inMemoryStorage.js.map +1 -1
- package/dist/memory/nodeSqliteStorage.d.ts +17 -0
- package/dist/memory/nodeSqliteStorage.d.ts.map +1 -0
- package/dist/memory/nodeSqliteStorage.js +393 -0
- package/dist/memory/nodeSqliteStorage.js.map +1 -0
- package/dist/memory/projectManager.d.ts +10 -20
- package/dist/memory/projectManager.d.ts.map +1 -1
- package/dist/memory/projectManager.js +27 -41
- package/dist/memory/projectManager.js.map +1 -1
- package/dist/memory/roundStore.d.ts +76 -1
- package/dist/memory/roundStore.d.ts.map +1 -1
- package/dist/memory/roundStore.js.map +1 -1
- package/dist/memory/roundSummaryLookup.d.ts +26 -0
- package/dist/memory/roundSummaryLookup.d.ts.map +1 -0
- package/dist/memory/roundSummaryLookup.js +19 -0
- package/dist/memory/roundSummaryLookup.js.map +1 -0
- package/dist/memory/sessionStore.d.ts +19 -2
- package/dist/memory/sessionStore.d.ts.map +1 -1
- package/dist/memory/sessionStore.js.map +1 -1
- package/dist/memory/sourceValidation.d.ts +2 -13
- package/dist/memory/sourceValidation.d.ts.map +1 -1
- package/dist/memory/sourceValidation.js +5 -16
- package/dist/memory/sourceValidation.js.map +1 -1
- package/dist/memory/storageInterface.d.ts +26 -16
- package/dist/memory/storageInterface.d.ts.map +1 -1
- package/dist/memory/supersedeResolve.d.ts +39 -0
- package/dist/memory/supersedeResolve.d.ts.map +1 -0
- package/dist/memory/supersedeResolve.js +16 -0
- package/dist/memory/supersedeResolve.js.map +1 -0
- package/dist/memory/types.d.ts +3 -2
- package/dist/memory/types.d.ts.map +1 -1
- package/dist/memory/types.js +2 -1
- package/dist/memory/types.js.map +1 -1
- package/dist/role-pack/rolePackManager.d.ts +33 -24
- package/dist/role-pack/rolePackManager.d.ts.map +1 -1
- package/dist/role-pack/rolePackManager.js +48 -42
- package/dist/role-pack/rolePackManager.js.map +1 -1
- package/dist/role-pack/strategyKeys.d.ts +8 -0
- package/dist/role-pack/strategyKeys.d.ts.map +1 -1
- package/dist/role-pack/strategyKeys.js +10 -0
- package/dist/role-pack/strategyKeys.js.map +1 -1
- package/dist/role-pack/strategyResolver.d.ts +6 -1
- package/dist/role-pack/strategyResolver.d.ts.map +1 -1
- package/dist/role-pack/strategyResolver.js +20 -2
- package/dist/role-pack/strategyResolver.js.map +1 -1
- package/dist/role-pack/types.d.ts +5 -1
- package/dist/role-pack/types.d.ts.map +1 -1
- package/dist/security/confirmEntries.d.ts +21 -6
- package/dist/security/confirmEntries.d.ts.map +1 -1
- package/dist/security/confirmEntries.js +21 -6
- package/dist/security/confirmEntries.js.map +1 -1
- package/dist/security/pathGuard.d.ts +44 -10
- package/dist/security/pathGuard.d.ts.map +1 -1
- package/dist/security/pathGuard.js +70 -10
- package/dist/security/pathGuard.js.map +1 -1
- package/dist/utils/backgroundTask.d.ts +56 -34
- package/dist/utils/backgroundTask.d.ts.map +1 -1
- package/dist/utils/backgroundTask.js +130 -99
- package/dist/utils/backgroundTask.js.map +1 -1
- package/dist/utils/errors.d.ts +3 -2
- package/dist/utils/errors.d.ts.map +1 -1
- package/dist/utils/errors.js +3 -2
- package/dist/utils/errors.js.map +1 -1
- package/dist/utils/eventEmitter.d.ts +20 -22
- package/dist/utils/eventEmitter.d.ts.map +1 -1
- package/dist/utils/eventEmitter.js +0 -3
- package/dist/utils/eventEmitter.js.map +1 -1
- package/dist/utils/time.d.ts +3 -3
- package/dist/utils/time.d.ts.map +1 -1
- package/dist/utils/time.js +3 -3
- package/dist/utils/time.js.map +1 -1
- package/dist/utils/toError.d.ts +1 -1
- package/dist/utils/toError.js +1 -1
- package/dist/web-fetch/loginWall.d.ts +39 -0
- package/dist/web-fetch/loginWall.d.ts.map +1 -0
- package/dist/web-fetch/loginWall.js +78 -0
- package/dist/web-fetch/loginWall.js.map +1 -0
- package/dist/web-fetch/types.d.ts +8 -0
- package/dist/web-fetch/types.d.ts.map +1 -1
- package/dist/web-fetch/webFetchProvider.d.ts.map +1 -1
- package/dist/web-fetch/webFetchProvider.js +3 -1
- package/dist/web-fetch/webFetchProvider.js.map +1 -1
- package/dist/web-search/fetchWebSearchProvider.js +28 -23
- package/dist/web-search/fetchWebSearchProvider.js.map +1 -1
- package/docs/architecture/role-pack-authoring-guide.md +7 -1
- package/docs/architecture/role-pack-spec.md +11 -6
- package/docs/memora-api-reference.md +1354 -1276
- package/docs/memora-/345/215/207/347/272/247/346/214/207/345/215/227-4.x-/345/210/260-5.0.0.md +385 -0
- package/docs/memora-/346/216/245/345/205/245/346/214/207/345/215/227.md +89 -49
- package/docs/role-pack-/345/274/200/346/224/276/351/224/256/346/214/207/345/215/227.md +148 -148
- package/package.json +13 -4
- package/role-packs/README.md +41 -40
- package/role-packs/memora/345/212/251/346/211/213/rules.md +5 -4
- package/role-packs//345/205/261/351/270/243/345/260/217/350/257/264/345/256/266/manifest.json +1 -0
- package/role-packs//345/205/261/351/270/243/345/260/217/350/257/264/345/256/266/persona.md +46 -46
- package/role-packs//345/205/261/351/270/243/345/260/217/350/257/264/345/256/266/rules.md +17 -16
- package/role-packs//345/205/261/351/270/243/345/260/217/350/257/264/345/256/266/skills/emotion-wave.md +46 -46
- package/role-packs//345/205/261/351/270/243/345/260/217/350/257/264/345/256/266/skills/foreshadow.md +29 -29
- package/role-packs//345/205/261/351/270/243/345/260/217/350/257/264/345/256/266/skills/outline-expand.md +73 -48
- package/role-packs//345/205/261/351/270/243/345/260/217/350/257/264/345/256/266/skills/project-init/scripts/init-novel-project.mjs +445 -441
- package/role-packs//345/205/261/351/270/243/345/260/217/350/257/264/345/256/266/skills/story-bible.md +62 -62
- package/role-packs//347/231/275/350/257/235/346/226/271/346/241/210/350/256/276/350/256/241/345/270/210/manifest.json +1 -0
- package/role-packs//347/231/275/350/257/235/346/226/271/346/241/210/350/256/276/350/256/241/345/270/210/rules.md +2 -1
- package/dist/agent/managers/dedupManager.d.ts +0 -85
- package/dist/agent/managers/dedupManager.d.ts.map +0 -1
- package/dist/agent/managers/dedupManager.js +0 -248
- package/dist/agent/managers/dedupManager.js.map +0 -1
- package/dist/agent/managers/goalConsistencyChecker.d.ts +0 -42
- package/dist/agent/managers/goalConsistencyChecker.d.ts.map +0 -1
- package/dist/agent/managers/goalConsistencyChecker.js +0 -178
- package/dist/agent/managers/goalConsistencyChecker.js.map +0 -1
- package/dist/agent/managers/llmJudgeHelper.d.ts +0 -21
- package/dist/agent/managers/llmJudgeHelper.d.ts.map +0 -1
- package/dist/agent/managers/llmJudgeHelper.js +0 -32
- package/dist/agent/managers/llmJudgeHelper.js.map +0 -1
- package/dist/agent/managers/memoryAdvisor.d.ts +0 -105
- package/dist/agent/managers/memoryAdvisor.d.ts.map +0 -1
- package/dist/agent/managers/memoryAdvisor.js +0 -322
- package/dist/agent/managers/memoryAdvisor.js.map +0 -1
- package/dist/agent/managers/memoryGovernance.d.ts +0 -24
- package/dist/agent/managers/memoryGovernance.d.ts.map +0 -1
- package/dist/agent/managers/memoryGovernance.js +0 -46
- package/dist/agent/managers/memoryGovernance.js.map +0 -1
- package/dist/memory/governance.d.ts +0 -17
- package/dist/memory/governance.d.ts.map +0 -1
- package/dist/memory/governance.js +0 -17
- package/dist/memory/governance.js.map +0 -1
- package/dist/memory/projectRegistry.d.ts +0 -47
- package/dist/memory/projectRegistry.d.ts.map +0 -1
- package/dist/memory/projectRegistry.js +0 -151
- package/dist/memory/projectRegistry.js.map +0 -1
- package/dist/utils/recallDefaults.d.ts +0 -16
- package/dist/utils/recallDefaults.d.ts.map +0 -1
- package/dist/utils/recallDefaults.js +0 -16
- package/dist/utils/recallDefaults.js.map +0 -1
|
@@ -8,7 +8,6 @@ import { chatBusyError, configError } from '../../utils/errors.js';
|
|
|
8
8
|
// 会话标识格式契约(SSOT):恢复最近会话时把 sessionId 拆成 (date, session) 供 loadMessages
|
|
9
9
|
import { isValidSessionId, splitSessionId } from '../../utils/time.js';
|
|
10
10
|
import { AGENT_CONSTANTS } from '../../agent/constants.js';
|
|
11
|
-
import { GoalConsistencyChecker } from '../../agent/managers/goalConsistencyChecker.js';
|
|
12
11
|
import { SessionStateMachine } from '../../agent/sessionStateMachine.js';
|
|
13
12
|
/** 会话管理器:经回调访问 Agent 当前组件状态,支持 Agent 重建后自动取最新引用 */
|
|
14
13
|
export class SessionManager {
|
|
@@ -24,17 +23,10 @@ export class SessionManager {
|
|
|
24
23
|
stateMachine;
|
|
25
24
|
/** 当前会话检查点(运行时状态快照) */
|
|
26
25
|
checkpoint = null;
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* 连续暂停时间戳数组(防滥用窗口):每次高风险暂停记录时间戳,窗口(1 小时)内连续 2 次则强制降为低风险暂停。
|
|
33
|
-
* ⚠ 进程内状态,不入 SessionCheckpoint——重启归零是设计(反滥用卫生状态,非执行状态);1h 窗口自愈防线,勿引入持久化。
|
|
34
|
-
*/
|
|
35
|
-
consecutivePauseTimestamps = [];
|
|
36
|
-
/** 连续暂停时间窗口(毫秒),1 小时前的暂停不计入连续计数 */
|
|
37
|
-
static CONSECUTIVE_PAUSE_WINDOW_MS = 3_600_000;
|
|
26
|
+
// 删后注记(2026-10-16 · 5.0.0):曾有「连续暂停防滥用」计数器(consecutivePauseTimestamps +
|
|
27
|
+
// CONSECUTIVE_PAUSE_WINDOW_MS)——整条机制从未通电:判据 isPauseLimitReached 零生产消费、
|
|
28
|
+
// 生产路径 pause() 恒传 lowRisk=true 使计数数组恒空,注释却谎称「≥2 次强制降级」。
|
|
29
|
+
// 按 SSOT/不带伤裁决拆除(见 CHANGELOG 5.0.0)。勿加回。
|
|
38
30
|
// ── 运行时暂停超时检测 ────────────────────────────────
|
|
39
31
|
/** 暂停超时检测定时器(只在 paused 状态时运行) */
|
|
40
32
|
_pauseTimeoutTimer = null;
|
|
@@ -47,7 +39,6 @@ export class SessionManager {
|
|
|
47
39
|
this.isChatBusy = isChatBusy;
|
|
48
40
|
this.emitEvent = emitEvent;
|
|
49
41
|
this.stateMachine = new SessionStateMachine('running');
|
|
50
|
-
this.consistencyChecker = new GoalConsistencyChecker();
|
|
51
42
|
}
|
|
52
43
|
// ─── SSOT 状态机代理(封装 stateMachine,暴露有界接口) ───
|
|
53
44
|
/** 会话状态(状态机状态) */
|
|
@@ -66,14 +57,9 @@ export class SessionManager {
|
|
|
66
57
|
get pendingPauseInfo() {
|
|
67
58
|
return this.stateMachine.pendingPauseInfo;
|
|
68
59
|
}
|
|
69
|
-
|
|
70
|
-
isPaused()
|
|
71
|
-
|
|
72
|
-
}
|
|
73
|
-
/** 检查是否处于运行态 */
|
|
74
|
-
isRunning() {
|
|
75
|
-
return this.stateMachine.status === 'running';
|
|
76
|
-
}
|
|
60
|
+
// 删后注记(2026-10-16 · 5.0.0,按 SSOT/不带伤裁决提前于 DEPRECATE-1 计划删除):曾有
|
|
61
|
+
// isPaused()/isRunning() 两个便捷布尔查询——判据与 status getter 重复(三态两布尔还吞掉 error 态),
|
|
62
|
+
// 内核+宿主+手册零消费的冗余别名。替代物 = `status`(`session.status === 'paused'`)。勿加回(§3.2.1)。
|
|
77
63
|
/** 待处理暂停请求是否在等待中 */
|
|
78
64
|
isPausePending() {
|
|
79
65
|
return this.stateMachine.isPausePending();
|
|
@@ -97,29 +83,38 @@ export class SessionManager {
|
|
|
97
83
|
}
|
|
98
84
|
/**
|
|
99
85
|
* 会话替换 chokepoint:作废所有会话级派生缓存。
|
|
100
|
-
* 任何替换 loop 工作记忆(switch/fork/
|
|
86
|
+
* 任何替换 loop 工作记忆(switch/fork/load)入口都必须经此,新增派生缓存在此注册一处避免"对称的另一半没写完"。
|
|
101
87
|
*/
|
|
102
88
|
invalidateSessionDerivedState() {
|
|
103
89
|
// 真实 loop 必有 resetContextSummary;测试 mock 可能未实现,缺失时 no-op
|
|
104
90
|
this.getLoop()?.resetContextSummary?.();
|
|
105
91
|
}
|
|
106
|
-
/**
|
|
107
|
-
|
|
108
|
-
|
|
92
|
+
/**
|
|
93
|
+
* 会话替换前置闸(SSOT 收口):任何替换 loop 工作记忆(switch/fork/load)的入口都必须经此。
|
|
94
|
+
* 合并「对话进行中」繁忙闸 + 「非 running 态拒绝」状态闸,消除 switch 有状态闸而
|
|
95
|
+
* fork/restore/load 漏闸的对称性缺口(原 switchSession 注释谎称「与 fork/restore 繁忙守卫同构」,
|
|
96
|
+
* 实际 fork/restore/load 无状态闸——属注释与实现不符的带伤,本次修复)。
|
|
97
|
+
* 暂停/错误态替换语义未定义,保守拒绝;宿主未就绪/暂停态触发由宿主 try/catch 兜底转错误通知。
|
|
98
|
+
*/
|
|
99
|
+
assertSessionReplaceable(operation) {
|
|
100
|
+
// 对话进行中替换会导致消息持久化分散 / loop 工作记忆被替换
|
|
109
101
|
if (this.isChatBusy()) {
|
|
110
|
-
throw chatBusyError(
|
|
102
|
+
throw chatBusyError(operation);
|
|
111
103
|
}
|
|
112
|
-
//
|
|
104
|
+
// 暂停/错误态替换语义未定义,保守拒绝(所有替换入口共用此闸,避免状态闸不对称)
|
|
113
105
|
if (this.stateMachine.status !== 'running') {
|
|
114
|
-
throw configError(
|
|
115
|
-
'仅 running
|
|
106
|
+
throw configError(operation, `会话处于 ${this.stateMachine.status} 态,无法替换`, [
|
|
107
|
+
'仅 running 态允许替换工作记忆',
|
|
116
108
|
]);
|
|
117
109
|
}
|
|
110
|
+
}
|
|
111
|
+
/** 切换当前会话。底层 MessageHistory.switchSession 为纯同步操作,不引入无意义 async 包装。 */
|
|
112
|
+
switchSession(newSession) {
|
|
113
|
+
// 替换工作记忆前置闸(SSOT:与 fork/load 共用断言,消除状态闸不对称)
|
|
114
|
+
this.assertSessionReplaceable('切换会话');
|
|
118
115
|
// 检查点是当前会话的工作状态,切换时清空内存态(检查点纯内存态,不落盘)
|
|
119
116
|
if (this.checkpoint && this.checkpoint.sessionId !== newSession) {
|
|
120
|
-
this.settleCheckpoint(true);
|
|
121
117
|
this.checkpoint = null;
|
|
122
|
-
this.checkpointDirty = false;
|
|
123
118
|
}
|
|
124
119
|
const result = this.getHistory().switchSession(newSession);
|
|
125
120
|
// 会话已替换:作废上下文摘要等派生缓存,避免陈旧摘要注入新会话
|
|
@@ -202,9 +197,8 @@ export class SessionManager {
|
|
|
202
197
|
* @param targetSession - 可选,自定义新会话名;不传则自动生成
|
|
203
198
|
*/
|
|
204
199
|
forkSession(roundId, targetSession) {
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
}
|
|
200
|
+
// 替换工作记忆前置闸(SSOT:与 switch/load 共用断言)
|
|
201
|
+
this.assertSessionReplaceable('分叉');
|
|
208
202
|
const history = this.getHistory();
|
|
209
203
|
// 记录源会话名(含日期前缀完整名,用于事件)
|
|
210
204
|
const sourceSessionName = history.currentSessionName;
|
|
@@ -214,11 +208,10 @@ export class SessionManager {
|
|
|
214
208
|
// round-based 模式:通过 roundIds 加载消息并应用到 loop
|
|
215
209
|
const sessionMessages = history.loadRoundBasedMessages(result.roundIds);
|
|
216
210
|
this.applySessionToLoop(sessionMessages);
|
|
217
|
-
// 关键约束:分叉后清空 checkpoint,防止新分支的 plan
|
|
211
|
+
// 关键约束:分叉后清空 checkpoint,防止新分支的 plan 等快照内容写入源会话检查点
|
|
212
|
+
// (曾连写 plan/goal,goal 三字段已随 5.0.0 GOAL-DRIFT-1 退役删除)。
|
|
218
213
|
if (this.checkpoint) {
|
|
219
|
-
this.settleCheckpoint(true);
|
|
220
214
|
this.checkpoint = null;
|
|
221
|
-
this.checkpointDirty = false;
|
|
222
215
|
}
|
|
223
216
|
this.emitEvent('sessionForked', {
|
|
224
217
|
from: sourceSessionName,
|
|
@@ -233,66 +226,21 @@ export class SessionManager {
|
|
|
233
226
|
roundIds: result.roundIds,
|
|
234
227
|
};
|
|
235
228
|
}
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
logger.debug({ hasSessionStore: false }, '未注入 ISessionStore,无法恢复会话');
|
|
252
|
-
return 0;
|
|
253
|
-
}
|
|
254
|
-
// SSOT:listSessionMetas 依 updatedAt 降序,[0] 即最近活跃会话(唯一时间序真理源)
|
|
255
|
-
const metas = this.sessionStore.listSessionMetas();
|
|
256
|
-
const target = metas[0]?.sessionId;
|
|
257
|
-
if (!target) {
|
|
258
|
-
logger.debug({ sessionCount: metas.length }, '没有可恢复的历史会话');
|
|
259
|
-
return 0;
|
|
260
|
-
}
|
|
261
|
-
// 解析 "YYYY-MM-DD-session"(sessionId 格式契约真理源 = splitSessionId,session 名可含连字符)
|
|
262
|
-
const parsed = splitSessionId(target);
|
|
263
|
-
if (!parsed.session) {
|
|
264
|
-
logger.debug({ session: target }, '会话标识格式不匹配');
|
|
265
|
-
return 0;
|
|
266
|
-
}
|
|
267
|
-
const sessionMessages = this.sessionStore.loadMessages(parsed.date, parsed.session);
|
|
268
|
-
if (sessionMessages.length === 0) {
|
|
269
|
-
logger.debug({ messageCount: 0 }, '没有找到可恢复的历史会话');
|
|
270
|
-
return 0;
|
|
271
|
-
}
|
|
272
|
-
this.applySessionToLoop(sessionMessages);
|
|
273
|
-
return sessionMessages.length;
|
|
274
|
-
}
|
|
275
|
-
/** 恢复指定会话的对话 */
|
|
276
|
-
async restoreSession(date, session) {
|
|
277
|
-
// 对话进行中恢复会导致 loop 工作记忆被替换
|
|
278
|
-
if (this.isChatBusy()) {
|
|
279
|
-
throw chatBusyError('恢复会话');
|
|
280
|
-
}
|
|
281
|
-
const sessionMessages = await this.getHistory().loadSessionMessages(date, session);
|
|
282
|
-
if (sessionMessages.length === 0) {
|
|
283
|
-
return 0;
|
|
284
|
-
}
|
|
285
|
-
this.applySessionToLoop(sessionMessages);
|
|
286
|
-
return sessionMessages.length;
|
|
287
|
-
}
|
|
288
|
-
/** 对外暴露的 loadSessionMessages 委托。⚠ 会切换当前会话(更新 currentDate/currentSession),加载后下一条 chat 写入被加载会话 */
|
|
289
|
-
async loadSessionMessages(date, session) {
|
|
290
|
-
// 对话进行中加载会切换 currentDate/currentSession,导致消息持久化分散
|
|
291
|
-
if (this.isChatBusy()) {
|
|
292
|
-
throw chatBusyError('加载会话');
|
|
293
|
-
}
|
|
294
|
-
return this.getHistory().loadSessionMessages(date, session);
|
|
295
|
-
}
|
|
229
|
+
// 删后注记(2026-10-16 · 5.0.0,按 SSOT/不带伤裁决提前于 DEPRECATE-1 周期删除):曾有公开
|
|
230
|
+
// restoreMostRecentSession() / restoreSession(date, session) 两个「从 ISessionStore 恢复会话到
|
|
231
|
+
// 工作记忆」入口——宿主零消费(宿主「打开最近/指定会话」走 chatPanel 读自家 HostSessionStore.
|
|
232
|
+
// listSessionMetas + switchToSession 对齐,不经内核恢复入口),且 listSessionMetas 降序契约的
|
|
233
|
+
// 真实消费者在宿主侧。恢复能力仍由 switchToSession(切换+加载,生产路径)与 forkSession 承担。
|
|
234
|
+
// 勿加回;若宿主将来要「重启后恢复内核 session 状态」,应基于 switchToSession 接线而非新开恢复面。
|
|
235
|
+
//
|
|
236
|
+
// 删后注记(2026-10-17 · 5.0.0,用户拍板不走 DEPRECATE-1 周期直删):曾有公开委托
|
|
237
|
+
// loadSessionMessages(date, session)——仅薄委托 MessageHistory.loadSessionMessages(设 currentDate/
|
|
238
|
+
// currentSession + 经 ISessionStore.loadMessages 读消息),是「半个 switchToSession」:改了会话锚点
|
|
239
|
+
// 却不 restore 到 AgentLoop 工作记忆,调用后下一条 chat 写入被加载会话而上下文仍属旧会话 =
|
|
240
|
+
// 语义不完整的陷阱入口(JSDoc ⚠ 即防此)。替代 = switchToSession(切换+加载+应用 loop 的生产路径);
|
|
241
|
+
// 「按 date/session 读消息」的能力真源 = ISessionStore.loadMessages 接口契约(宿主自家 store 已实现
|
|
242
|
+
// 并直读,chatPanel loadMessagesHistory 路径),不随本出口删除而消失。MessageHistory.
|
|
243
|
+
// loadSessionMessages 内部方法保留(switchToSession 内部消费,非公开面)。
|
|
296
244
|
/** 将会话消息恢复到 AgentLoop 工作记忆 */
|
|
297
245
|
applySessionToLoop(sessionMessages) {
|
|
298
246
|
const messages = sessionMessages.map((tm) => ({
|
|
@@ -304,25 +252,11 @@ export class SessionManager {
|
|
|
304
252
|
this.invalidateSessionDerivedState();
|
|
305
253
|
}
|
|
306
254
|
// ─── 不中断工作模型:检查点 + 状态机 ──────────────────
|
|
307
|
-
/**
|
|
308
|
-
* 检查点必需字段默认值工厂(默认值单一真理源)。
|
|
309
|
-
* 仅 createCheckpoint 新建/合并时使用——反序列化采用严格模式,不做静默补齐。
|
|
310
|
-
* 必须返回新实例:数组/对象默认值共享引用会让不同检查点互相污染(一个 push 计划任务项另一凭空多出)。
|
|
311
|
-
*/
|
|
312
|
-
static checkpointDefaults() {
|
|
313
|
-
return {
|
|
314
|
-
mainGoal: '',
|
|
315
|
-
currentGoal: '',
|
|
316
|
-
goalChangeSeq: 0,
|
|
317
|
-
plan: [],
|
|
318
|
-
};
|
|
319
|
-
}
|
|
320
255
|
/** 从当前运行时状态创建检查点:快照消息历史与会话标识生成 SessionCheckpoint(纯内存态,
|
|
321
256
|
* 不落盘——中止/断电走中断轮补全,运行时暂停同 turn 内存续跑) */
|
|
322
|
-
createCheckpoint(
|
|
257
|
+
createCheckpoint() {
|
|
323
258
|
const history = this.getHistory();
|
|
324
259
|
const prev = this.checkpoint;
|
|
325
|
-
const defaults = SessionManager.checkpointDefaults();
|
|
326
260
|
// 合并语义:以已有检查点为基底展开,仅覆写本次重算的字段。
|
|
327
261
|
// 【禁止改回对象字面量整体重建】——整体重建≈隐式字段白名单,任何未显式列出的字段每次 pause 被静默丢弃
|
|
328
262
|
// (整体重建会使 planItemLog/completedToolCalls/pauseMeta 静默归零,非幂等工具恢复后重复执行)
|
|
@@ -340,50 +274,29 @@ export class SessionManager {
|
|
|
340
274
|
recovered: false,
|
|
341
275
|
}
|
|
342
276
|
: undefined,
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
currentGoal: mainGoal ?? prev?.currentGoal ?? prev?.mainGoal ?? defaults.currentGoal,
|
|
346
|
-
goalChangeSeq: prev?.goalChangeSeq ?? defaults.goalChangeSeq,
|
|
347
|
-
plan: prev?.plan ?? defaults.plan,
|
|
277
|
+
// plan 缺省必须是新数组实例:默认值共享引用会让不同检查点互相污染(一个 push 计划任务项另一凭空多出)
|
|
278
|
+
plan: prev?.plan ?? [],
|
|
348
279
|
lastHeartbeat: Date.now(),
|
|
349
280
|
};
|
|
350
|
-
// 检查点内容已整体重算,强制清脏(纯内存,无落盘)
|
|
351
|
-
this.settleCheckpoint(true);
|
|
352
281
|
return this.checkpoint;
|
|
353
282
|
}
|
|
354
283
|
/**
|
|
355
|
-
*
|
|
356
|
-
*
|
|
284
|
+
* 刷新检查点心跳(内存写点统一入口)。所有修改 `this.checkpoint` 的方法都必须调用本方法,
|
|
285
|
+
* 不得直接赋值 `lastHeartbeat`——心跳是暂停超时判定的兜底依据,绕过本方法会让超时判定读到陈旧值。
|
|
286
|
+
* ⚠️ 检查点不落盘:本方法**只刷心跳,无脏标记**(脏标记随 2026-10-14 CHKPT-3 减法删除——
|
|
287
|
+
* 曾经的 `checkpointDirty` 无外部消费者,属纯内耗)。
|
|
357
288
|
*/
|
|
358
289
|
touchCheckpoint() {
|
|
359
290
|
if (!this.checkpoint)
|
|
360
291
|
return;
|
|
361
292
|
this.checkpoint.lastHeartbeat = Date.now(); // 心跳唯一写点
|
|
362
|
-
this.checkpointDirty = true;
|
|
363
|
-
}
|
|
364
|
-
/**
|
|
365
|
-
* 检查点脏标记清理(**纯内存态**):
|
|
366
|
-
* `SessionCheckpoint` 不落盘——中止/断电走「中断轮补全为完整 turn」而非跨重启恢复,
|
|
367
|
-
* 运行时暂停是同 turn 内续跑(内存态),无跨进程载体需求。故本方法仅清理脏标记,
|
|
368
|
-
* 不再写存储层(保留方法语义边界,避免为纯净化波及 40+ 处调用点)。
|
|
369
|
-
*/
|
|
370
|
-
settleCheckpoint(force = false) {
|
|
371
|
-
if (!this.checkpoint)
|
|
372
|
-
return;
|
|
373
|
-
if (!force && !this.checkpointDirty)
|
|
374
|
-
return;
|
|
375
|
-
// 纯内存:无落盘动作,仅清脏
|
|
376
|
-
this.checkpointDirty = false;
|
|
377
293
|
}
|
|
378
294
|
/** 获取当前检查点快照;未创建返回 null */
|
|
379
295
|
getCheckpoint() {
|
|
380
296
|
return this.checkpoint;
|
|
381
297
|
}
|
|
382
|
-
/**
|
|
383
|
-
|
|
384
|
-
* @param lowRisk 低风险暂停不计连续计数(默认 false)
|
|
385
|
-
*/
|
|
386
|
-
pause(reason, source = 'user', lowRisk = false) {
|
|
298
|
+
/** 暂停会话(用户/Agent/系统均可触发)。暂停前自动创建检查点。 */
|
|
299
|
+
pause(reason, source = 'user') {
|
|
387
300
|
const result = this.stateMachine.pause(reason, source);
|
|
388
301
|
if (result.allowed) {
|
|
389
302
|
// 先在 createCheckpoint 前记 pausedAt,检查点经 spread 继承,省去 pause() 内的二次补设分支
|
|
@@ -391,19 +304,16 @@ export class SessionManager {
|
|
|
391
304
|
this.checkpoint.pausedAt = Date.now();
|
|
392
305
|
}
|
|
393
306
|
this.createCheckpoint();
|
|
394
|
-
// 首轮暂停无现有检查点,pausedAt 未被 spread
|
|
307
|
+
// 首轮暂停无现有检查点,pausedAt 未被 spread 继承,补设并刷心跳
|
|
395
308
|
if (this.checkpoint && !this.checkpoint.pausedAt) {
|
|
396
309
|
this.checkpoint.pausedAt = Date.now();
|
|
397
310
|
this.touchCheckpoint();
|
|
398
311
|
}
|
|
399
|
-
//
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
}
|
|
404
|
-
else {
|
|
405
|
-
logger.debug({ reason }, '低风险暂停不记录时间戳(Agent 自动兜底)');
|
|
406
|
-
}
|
|
312
|
+
// pauseMeta 唯一写入点(SSOT):两路暂停——用户 requestPause / ask_user 提问挂起——
|
|
313
|
+
// 全部经本方法翻状态机,故收敛到此处即可全覆盖,各调用方不再各自补写。
|
|
314
|
+
// ⚠ 消费方是**判据**不是展示:clearPlanOnTurnEnd 以 `cp.pauseMeta` 有无决定
|
|
315
|
+
// 「turn 是否真正结束、任务表该不该清」。任一暂停路径漏写,恢复后任务表会被误清。
|
|
316
|
+
this.setPauseMeta({ reason, source });
|
|
407
317
|
// 启动暂停超时检测,防暂停会话长时间占用资源
|
|
408
318
|
this.startPauseTimeoutTimer();
|
|
409
319
|
this.emitEvent('sessionPaused', {
|
|
@@ -411,7 +321,7 @@ export class SessionManager {
|
|
|
411
321
|
source,
|
|
412
322
|
sessionId: this.checkpoint?.sessionId,
|
|
413
323
|
});
|
|
414
|
-
logger.info({ reason, source
|
|
324
|
+
logger.info({ reason, source }, '会话已暂停');
|
|
415
325
|
}
|
|
416
326
|
return result.allowed;
|
|
417
327
|
}
|
|
@@ -440,8 +350,6 @@ export class SessionManager {
|
|
|
440
350
|
// 走 setPauseMeta 唯一写入口而非内联赋值,避免绕过其兜底链路(其内部已含 touch+settle,此处不再重复调用)
|
|
441
351
|
this.setPauseMeta(undefined);
|
|
442
352
|
}
|
|
443
|
-
// 连续暂停计数在暂停超时处理(checkPauseTimeout)对称重置,
|
|
444
|
-
// 不在 resume() 里清零——避免用户回答前过早清零导致防滥用机制无效
|
|
445
353
|
this.emitEvent('sessionResumed', {
|
|
446
354
|
sessionId: this.checkpoint?.sessionId,
|
|
447
355
|
});
|
|
@@ -462,38 +370,12 @@ export class SessionManager {
|
|
|
462
370
|
}
|
|
463
371
|
return result.allowed;
|
|
464
372
|
}
|
|
465
|
-
// ── 防滥用:连续暂停计数 ───────────────────────────────
|
|
466
|
-
/** 清理过期暂停时间戳(窗口过滤):移除超过窗口(1 小时)的旧时间戳,仅保留窗口内暂停;每次查询计数时自动调用 */
|
|
467
|
-
pruneStalePauseTimestamps() {
|
|
468
|
-
const cutoff = Date.now() - SessionManager.CONSECUTIVE_PAUSE_WINDOW_MS;
|
|
469
|
-
const before = this.consecutivePauseTimestamps.length;
|
|
470
|
-
this.consecutivePauseTimestamps = this.consecutivePauseTimestamps.filter((ts) => ts > cutoff);
|
|
471
|
-
const pruned = before - this.consecutivePauseTimestamps.length;
|
|
472
|
-
if (pruned > 0) {
|
|
473
|
-
logger.debug({ pruned, remaining: this.consecutivePauseTimestamps.length }, '过期暂停时间戳已清理');
|
|
474
|
-
}
|
|
475
|
-
}
|
|
476
|
-
/** 检查连续暂停是否已达上限(防滥用):先清理窗口外时间戳,再查窗口内暂停≥2 次则强制降为低风险暂停。低风险不计数 */
|
|
477
|
-
isPauseLimitReached() {
|
|
478
|
-
this.pruneStalePauseTimestamps();
|
|
479
|
-
return this.consecutivePauseTimestamps.length >= 2;
|
|
480
|
-
}
|
|
481
|
-
/** 获取当前连续暂停计数:先清理窗口外时间戳,返回窗口内有效暂停数 */
|
|
482
|
-
getConsecutivePauseCount() {
|
|
483
|
-
this.pruneStalePauseTimestamps();
|
|
484
|
-
return this.consecutivePauseTimestamps.length;
|
|
485
|
-
}
|
|
486
|
-
/** 重置连续暂停计数(防滥用):暂停超时处理后调用。与 resume() 分离,防回答前过早清零致防滥用失效 */
|
|
487
|
-
resetConsecutivePauseCount() {
|
|
488
|
-
this.consecutivePauseTimestamps = [];
|
|
489
|
-
logger.debug('连续暂停时间戳已清空');
|
|
490
|
-
}
|
|
491
373
|
/**
|
|
492
374
|
* 放弃当前暂停检查点(宿主 stop 语义的内核出口)
|
|
493
375
|
*
|
|
494
376
|
* 语义 = 用户决定**彻底放弃**暂停执行,不再 resume。清理内容:
|
|
495
377
|
* 1. 停暂停超时定时器(防止已放弃的 checkpoint 继续被超时清理)
|
|
496
|
-
* 2. 清内存态 checkpoint +
|
|
378
|
+
* 2. 清内存态 checkpoint + pauseMeta
|
|
497
379
|
* 3. 状态机 resetToRunning(回到 idle 之前的 running 基础态,下次 chat() 正常)
|
|
498
380
|
*
|
|
499
381
|
* 与 resume() 的区别:resume = 想继续跑暂停点;discardCheckpoint = 不想了,检查点作废。
|
|
@@ -511,7 +393,6 @@ export class SessionManager {
|
|
|
511
393
|
this.stopPauseTimeoutTimer();
|
|
512
394
|
// 2. 清内存态(检查点纯内存态不落盘,无存储清理动作)
|
|
513
395
|
this.checkpoint = null;
|
|
514
|
-
this.checkpointDirty = false;
|
|
515
396
|
this.setPauseMeta(undefined);
|
|
516
397
|
// 3. 状态机回到 running
|
|
517
398
|
this.stateMachine.resetToRunning();
|
|
@@ -532,8 +413,6 @@ export class SessionManager {
|
|
|
532
413
|
this.checkpoint.status = this.stateMachine.status;
|
|
533
414
|
this.touchCheckpoint();
|
|
534
415
|
}
|
|
535
|
-
// 内存态收口(无落盘:recovered 只活内存,跨进程一律走中断补全)
|
|
536
|
-
this.settleCheckpoint();
|
|
537
416
|
this.emitEvent('sessionRecovered', {
|
|
538
417
|
sessionId: this.checkpoint?.sessionId,
|
|
539
418
|
});
|
|
@@ -541,52 +420,6 @@ export class SessionManager {
|
|
|
541
420
|
}
|
|
542
421
|
return result.allowed;
|
|
543
422
|
}
|
|
544
|
-
/**
|
|
545
|
-
* 更新检查点目标版本:递增 goalChangeSeq 触发漂移检测。漂移处置由内核自兜底不依赖宿主:
|
|
546
|
-
* drift 级内核直接低风险暂停强制确认;minor 级仅发射事件+日志不打断执行。
|
|
547
|
-
* goalDriftDetected 事件为可观测性出口,宿主可选订阅,不订阅不影响正确性。
|
|
548
|
-
*/
|
|
549
|
-
updateGoal(newGoal) {
|
|
550
|
-
if (!this.checkpoint) {
|
|
551
|
-
this.createCheckpoint(newGoal);
|
|
552
|
-
return null;
|
|
553
|
-
}
|
|
554
|
-
// 幂等短路:目标值未变直接返回(返回 level='same'),不递增 goalChangeSeq、不触发漂移。
|
|
555
|
-
// 续跑/补充输入每轮喂相同 currentGoal,若无短路 goalChangeSeq 会退化为轮次计数器
|
|
556
|
-
if (newGoal === this.checkpoint.currentGoal) {
|
|
557
|
-
return {
|
|
558
|
-
level: 'same',
|
|
559
|
-
similarity: 1,
|
|
560
|
-
constraints: this.consistencyChecker.extractConstraints(this.checkpoint.mainGoal),
|
|
561
|
-
constraintsConsistent: true,
|
|
562
|
-
};
|
|
563
|
-
}
|
|
564
|
-
// 执行一致性校验,与 mainGoal 对比
|
|
565
|
-
const mainGoal = this.checkpoint.mainGoal;
|
|
566
|
-
const consistencyResult = this.consistencyChecker.checkConsistency(mainGoal, newGoal);
|
|
567
|
-
this.checkpoint.currentGoal = newGoal;
|
|
568
|
-
this.checkpoint.goalChangeSeq++;
|
|
569
|
-
this.touchCheckpoint();
|
|
570
|
-
// 目标变更是会话的语义骨架,立即清脏(纯内存,无落盘)
|
|
571
|
-
this.settleCheckpoint();
|
|
572
|
-
// 检测到漂移则发射 goalDriftDetected 事件;drift 级自动低风险暂停强制用户确认(不计入连续计数)
|
|
573
|
-
if (consistencyResult.level !== 'same') {
|
|
574
|
-
this.emitEvent('goalDriftDetected', {
|
|
575
|
-
sessionId: this.checkpoint.sessionId,
|
|
576
|
-
mainGoal,
|
|
577
|
-
newGoal,
|
|
578
|
-
similarity: consistencyResult.similarity,
|
|
579
|
-
level: consistencyResult.level,
|
|
580
|
-
constraints: consistencyResult.constraints,
|
|
581
|
-
goalChangeSeq: this.checkpoint.goalChangeSeq,
|
|
582
|
-
});
|
|
583
|
-
if (consistencyResult.level === 'drift') {
|
|
584
|
-
const pauseReason = `目标漂移:新目标与原始目标不一致(相似度 ${consistencyResult.similarity.toFixed(2)})`;
|
|
585
|
-
this.pause(pauseReason, 'system', true);
|
|
586
|
-
}
|
|
587
|
-
}
|
|
588
|
-
return consistencyResult;
|
|
589
|
-
}
|
|
590
423
|
/** 全量替换计划任务项(与 appendPlanItem 仅追加正交;保留已有任务项 id/status)。唯一生产点:task_table_write mode='replace' */
|
|
591
424
|
updatePlan(plan) {
|
|
592
425
|
if (!this.checkpoint)
|
|
@@ -655,8 +488,7 @@ export class SessionManager {
|
|
|
655
488
|
return cp.plan;
|
|
656
489
|
}
|
|
657
490
|
/**
|
|
658
|
-
* 更新计划任务项状态(plan
|
|
659
|
-
* 否则状态变更可能丢失标脏,计划变更与标脏在此原子完成。
|
|
491
|
+
* 更新计划任务项状态(plan 任务项状态的唯一写点)。
|
|
660
492
|
*
|
|
661
493
|
* 写完后自动 ensureActivePlanItem:如果变更导致 active 空缺(如把 active 标记为 done/blocked),
|
|
662
494
|
* 则推进下一个 pending → active。这保证任何时刻 plan 中恰好有一个 active 任务项。
|
|
@@ -697,15 +529,14 @@ export class SessionManager {
|
|
|
697
529
|
const firstPending = plan.find((s) => s.status === 'pending');
|
|
698
530
|
if (firstPending) {
|
|
699
531
|
firstPending.status = 'active';
|
|
700
|
-
// 不 touchCheckpoint——调用方(updatePlanItemStatus / writePlan
|
|
532
|
+
// 不 touchCheckpoint——调用方(updatePlanItemStatus / writePlan)会统一刷心跳
|
|
701
533
|
}
|
|
702
534
|
}
|
|
703
535
|
/**
|
|
704
536
|
* 是否存在「在途计划」= 有未完成任务项(pending 或 active)。
|
|
705
537
|
*
|
|
706
|
-
* SSOT
|
|
707
|
-
* 任务表 nudge 跳过(loop
|
|
708
|
-
* 可续跑信号)。禁任一消费方自行内联谓词。
|
|
538
|
+
* SSOT:全库该命题唯一实现。消费方两处——会议骨架预置守卫(prepare,判断续会不重开)、
|
|
539
|
+
* 任务表 nudge 跳过(loop,已有在途表则不再诱导建表)。禁任一消费方自行内联谓词。
|
|
709
540
|
*
|
|
710
541
|
* 口径取 pending||active(非仅 active):与 ensureActivePlanItem 的补偿语义对齐——
|
|
711
542
|
* plan 非空时恒有一个 active(见 :826 场景 1/2),全 done/blocked 时无 active(场景 3),
|
|
@@ -727,9 +558,8 @@ export class SessionManager {
|
|
|
727
558
|
this.updatePlanItemStatus(planItemId, 'done');
|
|
728
559
|
}
|
|
729
560
|
this.appendPlanItemLog({ planItemId, summary });
|
|
730
|
-
//
|
|
561
|
+
// 心跳刷新:step 边界即检查点语义边界(纯内存态;检查点不落盘,崩溃走中断轮补全)
|
|
731
562
|
this.touchCheckpoint();
|
|
732
|
-
this.settleCheckpoint();
|
|
733
563
|
}
|
|
734
564
|
/** 写 step 边界日志(时间轴投影,不改 plan 状态)。迭代边界
|
|
735
565
|
* onPlanItemBoundary 只做本写——plan 状态推进唯一写者 = LLM 的 task_table_update。 */
|
|
@@ -738,7 +568,6 @@ export class SessionManager {
|
|
|
738
568
|
return;
|
|
739
569
|
this.appendPlanItemLog(options);
|
|
740
570
|
this.touchCheckpoint();
|
|
741
|
-
this.settleCheckpoint();
|
|
742
571
|
}
|
|
743
572
|
/**
|
|
744
573
|
* 兜底收尾(「LLM 未显式 update 即收尾」):turn 正常完成且计划已「全部到达」——
|
|
@@ -800,9 +629,8 @@ export class SessionManager {
|
|
|
800
629
|
this.checkpoint.plan = [];
|
|
801
630
|
this.checkpoint.planItemLog = undefined;
|
|
802
631
|
this.touchCheckpoint();
|
|
803
|
-
this.settleCheckpoint();
|
|
804
632
|
}
|
|
805
|
-
/** 设置暂停元数据;传 undefined
|
|
633
|
+
/** 设置暂停元数据;传 undefined 清除。写后刷心跳(纯内存态无磁盘残留) */
|
|
806
634
|
setPauseMeta(meta) {
|
|
807
635
|
// 意图:pauseMeta 通常随 pause()(建检查点后)写入,但检查点不一定已存在(首轮/未知状态),
|
|
808
636
|
// 故保留防御性守卫——无检查点时静默 no-op,不抛弃 pause/resume 链路的其余语义。
|
|
@@ -810,7 +638,6 @@ export class SessionManager {
|
|
|
810
638
|
return;
|
|
811
639
|
this.checkpoint.pauseMeta = meta;
|
|
812
640
|
this.touchCheckpoint();
|
|
813
|
-
this.settleCheckpoint();
|
|
814
641
|
}
|
|
815
642
|
/**
|
|
816
643
|
* 计划是否全部阻塞(预判短路收窄专用判定):仅全 blocked 视为真停滞。
|
|
@@ -839,7 +666,6 @@ export class SessionManager {
|
|
|
839
666
|
// ── 工具执行日志(outbox 模式):补偿管线不启用,以下仅日志 ──
|
|
840
667
|
/**
|
|
841
668
|
* 记录工具执行:追加到检查点日志(append-only,内存态)。
|
|
842
|
-
* 标脏策略:仅标脏不即时清脏(completePlanItem step 边界统一清脏、createCheckpoint 暂停/异常强制清脏)。
|
|
843
669
|
* 检查点纯内存态不落盘:completedToolCalls 仅内存态,回合中途崩溃即整体丢弃、走中断轮补全恢复;
|
|
844
670
|
* 「工具重跑排重」仅在单进程存活期内有效,无跨重启持久化。
|
|
845
671
|
*/
|
|
@@ -859,28 +685,15 @@ export class SessionManager {
|
|
|
859
685
|
}
|
|
860
686
|
}
|
|
861
687
|
this.touchCheckpoint();
|
|
862
|
-
// 依赖 completePlanItem / createCheckpoint 在 step 边界统一清脏
|
|
863
|
-
}
|
|
864
|
-
/** 记录非幂等工具执行(补偿降级后仅日志):不再逐副作用执行补偿,仅记录事实供宿主/人工排查 */
|
|
865
|
-
compensateTool(record) {
|
|
866
|
-
const msg = `${record.name}(${record.argsSignature.slice(0, 50)}):非幂等工具,需人工确认是否需要补偿`;
|
|
867
|
-
logger.warn({ tool: record.name, argsSignature: record.argsSignature.slice(0, 80) }, msg);
|
|
868
|
-
return msg;
|
|
869
|
-
}
|
|
870
|
-
/** 记录所有非幂等工具执行(补偿降级后仅日志):直接记录所有非幂等工具,每条生成一条日志 */
|
|
871
|
-
compensateAllNonIdempotent() {
|
|
872
|
-
if (!this.checkpoint?.completedToolCalls)
|
|
873
|
-
return [];
|
|
874
|
-
return this.checkpoint.completedToolCalls
|
|
875
|
-
.filter((r) => r.idempotent === 'non-idempotent')
|
|
876
|
-
.map((r) => this.compensateTool(r));
|
|
877
|
-
}
|
|
878
|
-
/** 发送心跳,防僵尸会话 */
|
|
879
|
-
heartbeat() {
|
|
880
|
-
if (this.checkpoint) {
|
|
881
|
-
this.touchCheckpoint();
|
|
882
|
-
}
|
|
883
688
|
}
|
|
689
|
+
// 删后注记(2026-10-16 · 5.0.0,按 SSOT/不带伤裁决提前于 DEPRECATE-1 周期删除):曾有公开
|
|
690
|
+
// compensateTool() / compensateAllNonIdempotent()——「非幂等工具补偿」降级后仅生成文案 +
|
|
691
|
+
// logger.warn,零状态变更、零返回值消费者、生产零调用(仅测试直测),属 no-op 型公开面。
|
|
692
|
+
// 勿加回;真要做人工确认面,应经 tool_start/tool_result 事件流在宿主侧渲染。
|
|
693
|
+
// 删后注记(2026-10-16 · 5.0.0,按 SSOT/不带伤裁决提前于 DEPRECATE-1 计划删除):曾有公开
|
|
694
|
+
// heartbeat()——把私有 touchCheckpoint(心跳唯一写点)包成公开方法,暗示接入者需手动打卡;
|
|
695
|
+
// 实际心跳由每个修改检查点的方法自动刷新,且超时判定时钟钉在 pausedAt(设计已排除手动保活),
|
|
696
|
+
// 生产+宿主零调用。勿加回;真要「暂停保活」需求应开专用 API(见台账 DEPRECATE-1 销项记录)。
|
|
884
697
|
/**
|
|
885
698
|
* 标记会话暂停超时(超时事实唯一写点)。广播式:超时信息经事件载荷传递,多监听器并行消费互不干扰。
|
|
886
699
|
* 由 checkPauseTimeout 调用(唯一调用点,启动路径无跨重启恢复)。
|
|
@@ -929,28 +742,14 @@ export class SessionManager {
|
|
|
929
742
|
const sessionId = checkpoint.sessionId;
|
|
930
743
|
const pauseDuration = Date.now() - (checkpoint.pausedAt ?? checkpoint.lastHeartbeat);
|
|
931
744
|
logger.warn({ sessionId, pauseDuration }, '运行时检测到暂停超时,自动清理检查点');
|
|
932
|
-
// 清除检查点 + 状态机回到 running
|
|
745
|
+
// 清除检查点 + 状态机回到 running
|
|
933
746
|
this.checkpoint = null;
|
|
934
|
-
this.checkpointDirty = false;
|
|
935
747
|
this.stateMachine.resetToRunning();
|
|
936
|
-
// 暂停超时意味着会话断裂,重置连续暂停计数(递增→衰减→重置闭合)
|
|
937
|
-
this.resetConsecutivePauseCount();
|
|
938
748
|
// 记录超时会话 + 发射事件(唯一入口)
|
|
939
749
|
this.markSessionTimedOut(sessionId, pauseDuration);
|
|
940
750
|
// 超时后不再需要继续检测
|
|
941
751
|
this.stopPauseTimeoutTimer();
|
|
942
752
|
}
|
|
943
|
-
/** 关闭时清脏(纯内存,无落盘):Agent 关闭、destroy 前调用。检查点不落盘,
|
|
944
|
-
* 本方法为保留语义边界的 no-op 清脏(无持久化动作) */
|
|
945
|
-
flushOnShutdown() {
|
|
946
|
-
this.settleCheckpoint(true);
|
|
947
|
-
}
|
|
948
|
-
/** 立即清脏(纯内存,无落盘):非只读工具完成后调用。⚠️ 检查点不落盘,completedToolCalls 仅内存态——
|
|
949
|
-
* 「工具重跑排重」仅在单进程存活期内生效,进程崩溃/重启后失效(走中断轮补全)。无落盘设计,
|
|
950
|
-
* 本方法为保留语义边界的 no-op 清脏 */
|
|
951
|
-
flushNow() {
|
|
952
|
-
this.settleCheckpoint(true);
|
|
953
|
-
}
|
|
954
753
|
/** 销毁 SessionManager,清理所有定时器(防阻止进程退出或悬空回调) */
|
|
955
754
|
destroy() {
|
|
956
755
|
this.stopPauseTimeoutTimer();
|