@zooique/memora 2.0.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1028 -0
- package/README.en.md +305 -0
- package/README.md +303 -148
- package/config.example.json +43 -0
- package/dist/agent/agent.d.ts +438 -295
- package/dist/agent/agent.d.ts.map +1 -1
- package/dist/agent/agent.js +1368 -836
- package/dist/agent/agent.js.map +1 -1
- package/dist/agent/assembler.d.ts +117 -39
- package/dist/agent/assembler.d.ts.map +1 -1
- package/dist/agent/assembler.js +574 -71
- package/dist/agent/assembler.js.map +1 -1
- package/dist/agent/budget.d.ts +131 -0
- package/dist/agent/budget.d.ts.map +1 -0
- package/dist/agent/budget.js +128 -0
- package/dist/agent/budget.js.map +1 -0
- package/dist/agent/builtinToolHandlers.d.ts +229 -17
- package/dist/agent/builtinToolHandlers.d.ts.map +1 -1
- package/dist/agent/builtinToolHandlers.js +758 -70
- package/dist/agent/builtinToolHandlers.js.map +1 -1
- package/dist/agent/builtinTools.d.ts +198 -7
- package/dist/agent/builtinTools.d.ts.map +1 -1
- package/dist/agent/builtinTools.js +646 -14
- package/dist/agent/builtinTools.js.map +1 -1
- package/dist/agent/compaction.d.ts +93 -0
- package/dist/agent/compaction.d.ts.map +1 -0
- package/dist/agent/compaction.js +192 -0
- package/dist/agent/compaction.js.map +1 -0
- package/dist/agent/constants.d.ts +139 -18
- package/dist/agent/constants.d.ts.map +1 -1
- package/dist/agent/constants.js +154 -18
- package/dist/agent/constants.js.map +1 -1
- package/dist/agent/contextManager.d.ts +126 -76
- package/dist/agent/contextManager.d.ts.map +1 -1
- package/dist/agent/contextManager.js +197 -135
- package/dist/agent/contextManager.js.map +1 -1
- package/dist/agent/contextPreparer.d.ts +78 -0
- package/dist/agent/contextPreparer.d.ts.map +1 -0
- package/dist/agent/contextPreparer.js +128 -0
- package/dist/agent/contextPreparer.js.map +1 -0
- package/dist/agent/duplicateInterceptor.d.ts +67 -0
- package/dist/agent/duplicateInterceptor.d.ts.map +1 -0
- package/dist/agent/duplicateInterceptor.js +82 -0
- package/dist/agent/duplicateInterceptor.js.map +1 -0
- package/dist/agent/guardRail.d.ts +166 -0
- package/dist/agent/guardRail.d.ts.map +1 -0
- package/dist/agent/guardRail.js +333 -0
- package/dist/agent/guardRail.js.map +1 -0
- package/dist/agent/loop.d.ts +718 -253
- package/dist/agent/loop.d.ts.map +1 -1
- package/dist/agent/loop.js +2215 -648
- package/dist/agent/loop.js.map +1 -1
- package/dist/agent/managers/archiveCoordinator.d.ts +21 -74
- package/dist/agent/managers/archiveCoordinator.d.ts.map +1 -1
- package/dist/agent/managers/archiveCoordinator.js +32 -105
- package/dist/agent/managers/archiveCoordinator.js.map +1 -1
- package/dist/agent/managers/chatLockManager.d.ts +20 -88
- package/dist/agent/managers/chatLockManager.d.ts.map +1 -1
- package/dist/agent/managers/chatLockManager.js +31 -103
- package/dist/agent/managers/chatLockManager.js.map +1 -1
- package/dist/agent/managers/dedupManager.d.ts +85 -0
- package/dist/agent/managers/dedupManager.d.ts.map +1 -0
- package/dist/agent/managers/dedupManager.js +248 -0
- package/dist/agent/managers/dedupManager.js.map +1 -0
- package/dist/agent/managers/goalConsistencyChecker.d.ts +42 -0
- package/dist/agent/managers/goalConsistencyChecker.d.ts.map +1 -0
- package/dist/agent/managers/goalConsistencyChecker.js +178 -0
- package/dist/agent/managers/goalConsistencyChecker.js.map +1 -0
- package/dist/agent/managers/llmCaller.d.ts +124 -0
- package/dist/agent/managers/llmCaller.d.ts.map +1 -0
- package/dist/agent/managers/llmCaller.js +417 -0
- package/dist/agent/managers/llmCaller.js.map +1 -0
- package/dist/agent/managers/llmJudgeHelper.d.ts +21 -0
- package/dist/agent/managers/llmJudgeHelper.d.ts.map +1 -0
- package/dist/agent/managers/llmJudgeHelper.js +32 -0
- package/dist/agent/managers/llmJudgeHelper.js.map +1 -0
- package/dist/agent/managers/loopMetrics.d.ts +58 -0
- package/dist/agent/managers/loopMetrics.d.ts.map +1 -0
- package/dist/agent/managers/loopMetrics.js +68 -0
- package/dist/agent/managers/loopMetrics.js.map +1 -0
- package/dist/agent/managers/memoryAdvisor.d.ts +27 -120
- package/dist/agent/managers/memoryAdvisor.d.ts.map +1 -1
- package/dist/agent/managers/memoryAdvisor.js +56 -192
- package/dist/agent/managers/memoryAdvisor.js.map +1 -1
- package/dist/agent/managers/memoryGovernance.d.ts +24 -0
- package/dist/agent/managers/memoryGovernance.d.ts.map +1 -0
- package/dist/agent/managers/memoryGovernance.js +46 -0
- package/dist/agent/managers/memoryGovernance.js.map +1 -0
- package/dist/agent/managers/memoryInspector.d.ts +59 -393
- package/dist/agent/managers/memoryInspector.d.ts.map +1 -1
- package/dist/agent/managers/memoryInspector.js +92 -736
- package/dist/agent/managers/memoryInspector.js.map +1 -1
- package/dist/agent/managers/roundSummaryGenerator.d.ts +56 -0
- package/dist/agent/managers/roundSummaryGenerator.d.ts.map +1 -0
- package/dist/agent/managers/roundSummaryGenerator.js +201 -0
- package/dist/agent/managers/roundSummaryGenerator.js.map +1 -0
- package/dist/agent/managers/sessionArchiver.d.ts +19 -63
- package/dist/agent/managers/sessionArchiver.d.ts.map +1 -1
- package/dist/agent/managers/sessionArchiver.js +83 -157
- package/dist/agent/managers/sessionArchiver.js.map +1 -1
- package/dist/agent/managers/sessionManager.d.ts +286 -57
- package/dist/agent/managers/sessionManager.d.ts.map +1 -1
- package/dist/agent/managers/sessionManager.js +846 -85
- package/dist/agent/managers/sessionManager.js.map +1 -1
- package/dist/agent/managers/sessionNamer.d.ts +35 -0
- package/dist/agent/managers/sessionNamer.d.ts.map +1 -0
- package/dist/agent/managers/sessionNamer.js +104 -0
- package/dist/agent/managers/sessionNamer.js.map +1 -0
- package/dist/agent/managers/streamAccumulator.d.ts +20 -0
- package/dist/agent/managers/streamAccumulator.d.ts.map +1 -0
- package/dist/agent/managers/streamAccumulator.js +15 -0
- package/dist/agent/managers/streamAccumulator.js.map +1 -0
- package/dist/agent/managers/textPolishManager.d.ts +8 -44
- package/dist/agent/managers/textPolishManager.d.ts.map +1 -1
- package/dist/agent/managers/textPolishManager.js +12 -47
- package/dist/agent/managers/textPolishManager.js.map +1 -1
- package/dist/agent/managers/toolCallHelpers.d.ts +105 -0
- package/dist/agent/managers/toolCallHelpers.d.ts.map +1 -0
- package/dist/agent/managers/toolCallHelpers.js +161 -0
- package/dist/agent/managers/toolCallHelpers.js.map +1 -0
- package/dist/agent/managers/workProjection.d.ts +57 -87
- package/dist/agent/managers/workProjection.d.ts.map +1 -1
- package/dist/agent/managers/workProjection.js +166 -246
- package/dist/agent/managers/workProjection.js.map +1 -1
- package/dist/agent/messageHistory.d.ts +134 -84
- package/dist/agent/messageHistory.d.ts.map +1 -1
- package/dist/agent/messageHistory.js +419 -133
- package/dist/agent/messageHistory.js.map +1 -1
- package/dist/agent/needsPlanning.d.ts +28 -0
- package/dist/agent/needsPlanning.d.ts.map +1 -0
- package/dist/agent/needsPlanning.js +68 -0
- package/dist/agent/needsPlanning.js.map +1 -0
- package/dist/agent/seed/index.d.ts +11 -0
- package/dist/agent/seed/index.d.ts.map +1 -0
- package/dist/agent/seed/index.js +10 -0
- package/dist/agent/seed/index.js.map +1 -0
- package/dist/agent/seed/orchestrator.d.ts +83 -0
- package/dist/agent/seed/orchestrator.d.ts.map +1 -0
- package/dist/agent/seed/orchestrator.js +260 -0
- package/dist/agent/seed/orchestrator.js.map +1 -0
- package/dist/agent/seed/prepare.d.ts +49 -0
- package/dist/agent/seed/prepare.d.ts.map +1 -0
- package/dist/agent/seed/prepare.js +107 -0
- package/dist/agent/seed/prepare.js.map +1 -0
- package/dist/agent/seed/types.d.ts +115 -0
- package/dist/agent/seed/types.d.ts.map +1 -0
- package/dist/agent/seed/types.js +15 -0
- package/dist/agent/seed/types.js.map +1 -0
- package/dist/agent/sessionStateMachine.d.ts +78 -0
- package/dist/agent/sessionStateMachine.d.ts.map +1 -0
- package/dist/agent/sessionStateMachine.js +182 -0
- package/dist/agent/sessionStateMachine.js.map +1 -0
- package/dist/agent/taskTableRenderer.d.ts +51 -0
- package/dist/agent/taskTableRenderer.d.ts.map +1 -0
- package/dist/agent/taskTableRenderer.js +115 -0
- package/dist/agent/taskTableRenderer.js.map +1 -0
- package/dist/agent/toolExecutor.d.ts +255 -9
- package/dist/agent/toolExecutor.d.ts.map +1 -1
- package/dist/agent/toolExecutor.js +956 -20
- package/dist/agent/toolExecutor.js.map +1 -1
- package/dist/agent/toolLedger.d.ts +120 -0
- package/dist/agent/toolLedger.d.ts.map +1 -0
- package/dist/agent/toolLedger.js +149 -0
- package/dist/agent/toolLedger.js.map +1 -0
- package/dist/agent/toolResultCache.d.ts +134 -0
- package/dist/agent/toolResultCache.d.ts.map +1 -0
- package/dist/agent/toolResultCache.js +243 -0
- package/dist/agent/toolResultCache.js.map +1 -0
- package/dist/agent/toolResultOffload.d.ts +59 -0
- package/dist/agent/toolResultOffload.d.ts.map +1 -0
- package/dist/agent/toolResultOffload.js +70 -0
- package/dist/agent/toolResultOffload.js.map +1 -0
- package/dist/agent/toolRunner.d.ts +59 -0
- package/dist/agent/toolRunner.d.ts.map +1 -0
- package/dist/agent/toolRunner.js +147 -0
- package/dist/agent/toolRunner.js.map +1 -0
- package/dist/agent/tracer.d.ts +99 -107
- package/dist/agent/tracer.d.ts.map +1 -1
- package/dist/agent/tracer.js +24 -69
- package/dist/agent/tracer.js.map +1 -1
- package/dist/agent/types.d.ts +512 -162
- package/dist/agent/types.d.ts.map +1 -1
- package/dist/agent/types.js +2 -51
- package/dist/agent/types.js.map +1 -1
- package/dist/code-exec/codeExecutionProvider.d.ts +16 -0
- package/dist/code-exec/codeExecutionProvider.d.ts.map +1 -0
- package/dist/code-exec/codeExecutionProvider.js +57 -0
- package/dist/code-exec/codeExecutionProvider.js.map +1 -0
- package/dist/code-exec/types.d.ts +37 -0
- package/dist/code-exec/types.d.ts.map +1 -0
- package/dist/code-exec/types.js +12 -0
- package/dist/code-exec/types.js.map +1 -0
- package/dist/config/expandEnvVars.d.ts +22 -0
- package/dist/config/expandEnvVars.d.ts.map +1 -0
- package/dist/config/expandEnvVars.js +38 -0
- package/dist/config/expandEnvVars.js.map +1 -0
- package/dist/config/loader.d.ts +59 -242
- package/dist/config/loader.d.ts.map +1 -1
- package/dist/config/loader.js +237 -194
- package/dist/config/loader.js.map +1 -1
- package/dist/index.d.ts +69 -74
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +93 -63
- package/dist/index.js.map +1 -1
- package/dist/llm/abortSignal.d.ts +2 -3
- package/dist/llm/abortSignal.d.ts.map +1 -1
- package/dist/llm/abortSignal.js +2 -3
- package/dist/llm/abortSignal.js.map +1 -1
- package/dist/llm/factory.d.ts +29 -25
- package/dist/llm/factory.d.ts.map +1 -1
- package/dist/llm/factory.js +34 -69
- package/dist/llm/factory.js.map +1 -1
- package/dist/llm/openaiCompatible.d.ts +25 -40
- package/dist/llm/openaiCompatible.d.ts.map +1 -1
- package/dist/llm/openaiCompatible.js +181 -129
- package/dist/llm/openaiCompatible.js.map +1 -1
- package/dist/llm/provider.d.ts +29 -42
- package/dist/llm/provider.d.ts.map +1 -1
- package/dist/llm/provider.js +11 -10
- package/dist/llm/provider.js.map +1 -1
- package/dist/llm/types.d.ts +54 -11
- package/dist/llm/types.d.ts.map +1 -1
- package/dist/llm/types.js +0 -5
- package/dist/llm/types.js.map +1 -1
- package/dist/logging/logger.d.ts +8 -29
- package/dist/logging/logger.d.ts.map +1 -1
- package/dist/logging/logger.js +43 -233
- package/dist/logging/logger.js.map +1 -1
- package/dist/logging/loggerInterface.d.ts +9 -8
- package/dist/logging/loggerInterface.d.ts.map +1 -1
- package/dist/memory/gcService.d.ts +164 -0
- package/dist/memory/gcService.d.ts.map +1 -0
- package/dist/memory/gcService.js +215 -0
- package/dist/memory/gcService.js.map +1 -0
- package/dist/memory/governance.d.ts +17 -0
- package/dist/memory/governance.d.ts.map +1 -0
- package/dist/memory/governance.js +17 -0
- package/dist/memory/governance.js.map +1 -0
- package/dist/memory/inMemoryRoundStore.d.ts +124 -0
- package/dist/memory/inMemoryRoundStore.d.ts.map +1 -0
- package/dist/memory/inMemoryRoundStore.js +216 -0
- package/dist/memory/inMemoryRoundStore.js.map +1 -0
- package/dist/memory/inMemorySessionStore.d.ts +92 -0
- package/dist/memory/inMemorySessionStore.d.ts.map +1 -0
- package/dist/memory/inMemorySessionStore.js +219 -0
- package/dist/memory/inMemorySessionStore.js.map +1 -0
- package/dist/memory/inMemorySessionViewLoader.d.ts +89 -0
- package/dist/memory/inMemorySessionViewLoader.d.ts.map +1 -0
- package/dist/memory/inMemorySessionViewLoader.js +183 -0
- package/dist/memory/inMemorySessionViewLoader.js.map +1 -0
- package/dist/memory/inMemoryStorage.d.ts +26 -141
- package/dist/memory/inMemoryStorage.d.ts.map +1 -1
- package/dist/memory/inMemoryStorage.js +48 -187
- package/dist/memory/inMemoryStorage.js.map +1 -1
- package/dist/memory/keywordsTouch.d.ts +20 -0
- package/dist/memory/keywordsTouch.d.ts.map +1 -0
- package/dist/memory/keywordsTouch.js +25 -0
- package/dist/memory/keywordsTouch.js.map +1 -0
- package/dist/memory/lockManager.d.ts +7 -2
- package/dist/memory/lockManager.d.ts.map +1 -1
- package/dist/memory/lockManager.js +35 -11
- package/dist/memory/lockManager.js.map +1 -1
- package/dist/memory/projectManager.d.ts +30 -128
- package/dist/memory/projectManager.d.ts.map +1 -1
- package/dist/memory/projectManager.js +39 -171
- package/dist/memory/projectManager.js.map +1 -1
- package/dist/memory/projectRegistry.d.ts +22 -37
- package/dist/memory/projectRegistry.d.ts.map +1 -1
- package/dist/memory/projectRegistry.js +78 -85
- package/dist/memory/projectRegistry.js.map +1 -1
- package/dist/memory/roundStore.d.ts +629 -0
- package/dist/memory/roundStore.d.ts.map +1 -0
- package/dist/memory/roundStore.js +116 -0
- package/dist/memory/roundStore.js.map +1 -0
- package/dist/memory/sessionStore.d.ts +70 -61
- package/dist/memory/sessionStore.d.ts.map +1 -1
- package/dist/memory/sessionStore.js +23 -1
- package/dist/memory/sessionStore.js.map +1 -1
- package/dist/memory/sessionViewLoader.d.ts +141 -0
- package/dist/memory/sessionViewLoader.d.ts.map +1 -0
- package/dist/memory/sessionViewLoader.js +65 -0
- package/dist/memory/sessionViewLoader.js.map +1 -0
- package/dist/memory/sourceValidation.d.ts +18 -9
- package/dist/memory/sourceValidation.d.ts.map +1 -1
- package/dist/memory/sourceValidation.js +35 -29
- package/dist/memory/sourceValidation.js.map +1 -1
- package/dist/memory/storageInterface.d.ts +45 -126
- package/dist/memory/storageInterface.d.ts.map +1 -1
- package/dist/memory/types.d.ts +82 -168
- package/dist/memory/types.d.ts.map +1 -1
- package/dist/memory/types.js +116 -79
- package/dist/memory/types.js.map +1 -1
- package/dist/project-search/projectSearchProvider.d.ts +29 -0
- package/dist/project-search/projectSearchProvider.d.ts.map +1 -0
- package/dist/project-search/projectSearchProvider.js +57 -0
- package/dist/project-search/projectSearchProvider.js.map +1 -0
- package/dist/project-search/terms.d.ts +14 -0
- package/dist/project-search/terms.d.ts.map +1 -0
- package/dist/project-search/terms.js +68 -0
- package/dist/project-search/terms.js.map +1 -0
- package/dist/project-search/types.d.ts +139 -0
- package/dist/project-search/types.d.ts.map +1 -0
- package/dist/project-search/types.js +8 -0
- package/dist/project-search/types.js.map +1 -0
- package/dist/role-pack/capabilityMap.d.ts +28 -0
- package/dist/role-pack/capabilityMap.d.ts.map +1 -0
- package/dist/role-pack/capabilityMap.js +56 -0
- package/dist/role-pack/capabilityMap.js.map +1 -0
- package/dist/role-pack/constants.d.ts +21 -0
- package/dist/role-pack/constants.d.ts.map +1 -0
- package/dist/role-pack/constants.js +21 -0
- package/dist/role-pack/constants.js.map +1 -0
- package/dist/role-pack/rolePackManager.d.ts +269 -0
- package/dist/role-pack/rolePackManager.d.ts.map +1 -0
- package/dist/role-pack/rolePackManager.js +1082 -0
- package/dist/role-pack/rolePackManager.js.map +1 -0
- package/dist/role-pack/strategyKeys.d.ts +100 -0
- package/dist/role-pack/strategyKeys.d.ts.map +1 -0
- package/dist/role-pack/strategyKeys.js +184 -0
- package/dist/role-pack/strategyKeys.js.map +1 -0
- package/dist/role-pack/strategyResolver.d.ts +109 -0
- package/dist/role-pack/strategyResolver.d.ts.map +1 -0
- package/dist/role-pack/strategyResolver.js +304 -0
- package/dist/role-pack/strategyResolver.js.map +1 -0
- package/dist/role-pack/types.d.ts +237 -0
- package/dist/role-pack/types.d.ts.map +1 -0
- package/dist/role-pack/types.js +8 -0
- package/dist/role-pack/types.js.map +1 -0
- package/dist/role-pack/validator.d.ts +38 -0
- package/dist/role-pack/validator.d.ts.map +1 -0
- package/dist/role-pack/validator.js +436 -0
- package/dist/role-pack/validator.js.map +1 -0
- package/dist/security/pathGuard.d.ts +69 -72
- package/dist/security/pathGuard.d.ts.map +1 -1
- package/dist/security/pathGuard.js +151 -131
- package/dist/security/pathGuard.js.map +1 -1
- package/dist/skill/skillLayer3.d.ts +91 -0
- package/dist/skill/skillLayer3.d.ts.map +1 -0
- package/dist/skill/skillLayer3.js +104 -0
- package/dist/skill/skillLayer3.js.map +1 -0
- package/dist/skill/skillManager.d.ts +127 -56
- package/dist/skill/skillManager.d.ts.map +1 -1
- package/dist/skill/skillManager.js +318 -163
- package/dist/skill/skillManager.js.map +1 -1
- package/dist/skill/skillScriptRunner.d.ts +97 -0
- package/dist/skill/skillScriptRunner.d.ts.map +1 -0
- package/dist/skill/skillScriptRunner.js +271 -0
- package/dist/skill/skillScriptRunner.js.map +1 -0
- package/dist/skill/types.d.ts +73 -17
- package/dist/skill/types.d.ts.map +1 -1
- package/dist/skill/types.js +5 -1
- package/dist/skill/types.js.map +1 -1
- package/dist/utils/array.d.ts +5 -6
- package/dist/utils/array.d.ts.map +1 -1
- package/dist/utils/array.js +6 -6
- package/dist/utils/array.js.map +1 -1
- package/dist/utils/atomicWrite.d.ts +8 -0
- package/dist/utils/atomicWrite.d.ts.map +1 -0
- package/dist/utils/atomicWrite.js +31 -0
- package/dist/utils/atomicWrite.js.map +1 -0
- package/dist/utils/backgroundTask.d.ts +38 -0
- package/dist/utils/backgroundTask.d.ts.map +1 -0
- package/dist/utils/backgroundTask.js +117 -0
- package/dist/utils/backgroundTask.js.map +1 -0
- package/dist/utils/configResourceManager.d.ts +79 -0
- package/dist/utils/configResourceManager.d.ts.map +1 -0
- package/dist/utils/configResourceManager.js +150 -0
- package/dist/utils/configResourceManager.js.map +1 -0
- package/dist/utils/errors.d.ts +48 -56
- package/dist/utils/errors.d.ts.map +1 -1
- package/dist/utils/errors.js +60 -58
- package/dist/utils/errors.js.map +1 -1
- package/dist/utils/eventEmitter.d.ts +141 -63
- package/dist/utils/eventEmitter.d.ts.map +1 -1
- package/dist/utils/eventEmitter.js +50 -35
- package/dist/utils/eventEmitter.js.map +1 -1
- package/dist/utils/fileSafe.d.ts +20 -0
- package/dist/utils/fileSafe.d.ts.map +1 -0
- package/dist/utils/fileSafe.js +56 -0
- package/dist/utils/fileSafe.js.map +1 -0
- package/dist/utils/frontmatter.d.ts +2 -3
- package/dist/utils/frontmatter.d.ts.map +1 -1
- package/dist/utils/frontmatter.js +6 -5
- package/dist/utils/frontmatter.js.map +1 -1
- package/dist/utils/hash.d.ts +12 -0
- package/dist/utils/hash.d.ts.map +1 -0
- package/dist/utils/hash.js +27 -0
- package/dist/utils/hash.js.map +1 -0
- package/dist/utils/http.d.ts +17 -0
- package/dist/utils/http.d.ts.map +1 -0
- package/dist/utils/http.js +29 -0
- package/dist/utils/http.js.map +1 -0
- package/dist/utils/json.d.ts +1 -1
- package/dist/utils/json.js +1 -1
- package/dist/utils/math.d.ts +34 -1
- package/dist/utils/math.d.ts.map +1 -1
- package/dist/utils/math.js +47 -14
- package/dist/utils/math.js.map +1 -1
- package/dist/utils/objects.d.ts +2 -3
- package/dist/utils/objects.d.ts.map +1 -1
- package/dist/utils/objects.js +2 -3
- package/dist/utils/objects.js.map +1 -1
- package/dist/utils/recallDefaults.d.ts +16 -0
- package/dist/utils/recallDefaults.d.ts.map +1 -0
- package/dist/utils/recallDefaults.js +16 -0
- package/dist/utils/recallDefaults.js.map +1 -0
- package/dist/utils/safeTimer.d.ts +8 -36
- package/dist/utils/safeTimer.d.ts.map +1 -1
- package/dist/utils/safeTimer.js +9 -37
- package/dist/utils/safeTimer.js.map +1 -1
- package/dist/utils/scanner.d.ts +97 -17
- package/dist/utils/scanner.d.ts.map +1 -1
- package/dist/utils/scanner.js +269 -47
- package/dist/utils/scanner.js.map +1 -1
- package/dist/utils/segmenter.d.ts +20 -29
- package/dist/utils/segmenter.d.ts.map +1 -1
- package/dist/utils/segmenter.js +230 -88
- package/dist/utils/segmenter.js.map +1 -1
- package/dist/utils/strings.d.ts +15 -32
- package/dist/utils/strings.d.ts.map +1 -1
- package/dist/utils/strings.js +25 -44
- package/dist/utils/strings.js.map +1 -1
- package/dist/utils/time.d.ts +37 -0
- package/dist/utils/time.d.ts.map +1 -1
- package/dist/utils/time.js +56 -0
- package/dist/utils/time.js.map +1 -1
- package/dist/utils/toError.d.ts +13 -1
- package/dist/utils/toError.d.ts.map +1 -1
- package/dist/utils/toError.js +18 -1
- package/dist/utils/toError.js.map +1 -1
- package/dist/web-fetch/fetchWebFetchProvider.d.ts +14 -0
- package/dist/web-fetch/fetchWebFetchProvider.d.ts.map +1 -0
- package/dist/web-fetch/fetchWebFetchProvider.js +69 -0
- package/dist/web-fetch/fetchWebFetchProvider.js.map +1 -0
- package/dist/web-fetch/types.d.ts +30 -0
- package/dist/web-fetch/types.d.ts.map +1 -0
- package/dist/web-fetch/types.js +7 -0
- package/dist/web-fetch/types.js.map +1 -0
- package/dist/web-fetch/webFetchProvider.d.ts +10 -0
- package/dist/web-fetch/webFetchProvider.d.ts.map +1 -0
- package/dist/web-fetch/webFetchProvider.js +28 -0
- package/dist/web-fetch/webFetchProvider.js.map +1 -0
- package/dist/web-search/fetchWebSearchProvider.d.ts +27 -0
- package/dist/web-search/fetchWebSearchProvider.d.ts.map +1 -0
- package/dist/web-search/fetchWebSearchProvider.js +199 -0
- package/dist/web-search/fetchWebSearchProvider.js.map +1 -0
- package/dist/web-search/types.d.ts +46 -0
- package/dist/web-search/types.d.ts.map +1 -0
- package/dist/web-search/types.js +2 -0
- package/dist/{persona → web-search}/types.js.map +1 -1
- package/dist/web-search/webSearchProvider.d.ts +10 -0
- package/dist/web-search/webSearchProvider.d.ts.map +1 -0
- package/dist/web-search/webSearchProvider.js +30 -0
- package/dist/web-search/webSearchProvider.js.map +1 -0
- package/docs/architecture/role-pack-authoring-guide.md +243 -0
- package/docs/architecture/role-pack-spec.md +767 -0
- package/docs/memora-api-reference.md +1175 -0
- package/docs/memora-/346/216/245/345/205/245/346/214/207/345/215/227.md +843 -0
- package/docs/role-pack-/345/274/200/346/224/276/351/224/256/346/214/207/345/215/227.md +148 -0
- package/package.json +105 -83
- package/role-packs/README.md +41 -0
- package/role-packs/memora/345/212/251/346/211/213/manifest.json +12 -0
- package/role-packs/memora/345/212/251/346/211/213/persona.md +12 -0
- package/role-packs/memora/345/212/251/346/211/213/rules.md +4 -0
- package/role-packs//345/205/261/351/270/243/345/260/217/350/257/264/345/256/266/manifest.json +44 -0
- package/role-packs//345/205/261/351/270/243/345/260/217/350/257/264/345/256/266/persona.md +46 -0
- package/role-packs//345/205/261/351/270/243/345/260/217/350/257/264/345/256/266/rules.md +15 -0
- package/role-packs//345/205/261/351/270/243/345/260/217/350/257/264/345/256/266/skills/character-sheet.md +34 -0
- package/role-packs//345/205/261/351/270/243/345/260/217/350/257/264/345/256/266/skills/core-extractor.md +37 -0
- package/role-packs//345/205/261/351/270/243/345/260/217/350/257/264/345/256/266/skills/craft-review.md +69 -0
- package/role-packs//345/205/261/351/270/243/345/260/217/350/257/264/345/256/266/skills/dialogue-craft.md +45 -0
- package/role-packs//345/205/261/351/270/243/345/260/217/350/257/264/345/256/266/skills/emotion-wave.md +46 -0
- package/role-packs//345/205/261/351/270/243/345/260/217/350/257/264/345/256/266/skills/empathy-craft.md +79 -0
- package/role-packs//345/205/261/351/270/243/345/260/217/350/257/264/345/256/266/skills/foreshadow.md +29 -0
- package/role-packs//345/205/261/351/270/243/345/260/217/350/257/264/345/256/266/skills/hotspot-mapping.md +121 -0
- package/role-packs//345/205/261/351/270/243/345/260/217/350/257/264/345/256/266/skills/idea-lever.md +42 -0
- package/role-packs//345/205/261/351/270/243/345/260/217/350/257/264/345/256/266/skills/logline-craft.md +47 -0
- package/role-packs//345/205/261/351/270/243/345/260/217/350/257/264/345/256/266/skills/longform-guard.md +64 -0
- package/role-packs//345/205/261/351/270/243/345/260/217/350/257/264/345/256/266/skills/need-finder.md +40 -0
- package/role-packs//345/205/261/351/270/243/345/260/217/350/257/264/345/256/266/skills/outline-expand.md +46 -0
- package/role-packs//345/205/261/351/270/243/345/260/217/350/257/264/345/256/266/skills/pacing-hook.md +34 -0
- package/role-packs//345/205/261/351/270/243/345/260/217/350/257/264/345/256/266/skills/polish-method.md +113 -0
- package/role-packs//345/205/261/351/270/243/345/260/217/350/257/264/345/256/266/skills/pov-discipline.md +79 -0
- package/role-packs//345/205/261/351/270/243/345/260/217/350/257/264/345/256/266/skills/project-init/SKILL.md +112 -0
- 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 +441 -0
- package/role-packs//345/205/261/351/270/243/345/260/217/350/257/264/345/256/266/skills/relationship-web.md +47 -0
- package/role-packs//345/205/261/351/270/243/345/260/217/350/257/264/345/256/266/skills/scene-craft.md +46 -0
- package/role-packs//345/205/261/351/270/243/345/260/217/350/257/264/345/256/266/skills/story-bible.md +60 -0
- package/role-packs//345/205/261/351/270/243/345/260/217/350/257/264/345/256/266/skills/story-structure.md +36 -0
- package/role-packs//345/205/261/351/270/243/345/260/217/350/257/264/345/256/266/skills/style-weaver.md +109 -0
- package/role-packs//345/205/261/351/270/243/345/260/217/350/257/264/345/256/266/skills/three-layer-check.md +27 -0
- package/role-packs//345/205/261/351/270/243/345/260/217/350/257/264/345/256/266/skills/world-builder.md +41 -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/manifest.json +48 -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/persona.md +73 -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 +19 -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/skills/api-doc-writer.md +40 -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/skills/doc-planner.md +42 -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/skills/nutrient-search.md +28 -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/skills/seed-finder.md +28 -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/skills/ssot-audit.md +34 -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/skills/style-guide.md +40 -0
- package/dist/agent/guardrail.d.ts +0 -47
- package/dist/agent/guardrail.d.ts.map +0 -1
- package/dist/agent/guardrail.js +0 -51
- package/dist/agent/guardrail.js.map +0 -1
- package/dist/agent/managers/autoConfigRefiner.d.ts +0 -39
- package/dist/agent/managers/autoConfigRefiner.d.ts.map +0 -1
- package/dist/agent/managers/autoConfigRefiner.js +0 -150
- package/dist/agent/managers/autoConfigRefiner.js.map +0 -1
- package/dist/agent/managers/configManager.d.ts +0 -125
- package/dist/agent/managers/configManager.d.ts.map +0 -1
- package/dist/agent/managers/configManager.js +0 -194
- package/dist/agent/managers/configManager.js.map +0 -1
- package/dist/agent/managers/insightExtractor.d.ts +0 -139
- package/dist/agent/managers/insightExtractor.d.ts.map +0 -1
- package/dist/agent/managers/insightExtractor.js +0 -401
- package/dist/agent/managers/insightExtractor.js.map +0 -1
- package/dist/agent/managers/memoryDecayScheduler.d.ts +0 -198
- package/dist/agent/managers/memoryDecayScheduler.d.ts.map +0 -1
- package/dist/agent/managers/memoryDecayScheduler.js +0 -349
- package/dist/agent/managers/memoryDecayScheduler.js.map +0 -1
- package/dist/agent/managers/relationBuilder.d.ts +0 -131
- package/dist/agent/managers/relationBuilder.d.ts.map +0 -1
- package/dist/agent/managers/relationBuilder.js +0 -192
- package/dist/agent/managers/relationBuilder.js.map +0 -1
- package/dist/agent/personaMatcher.d.ts +0 -29
- package/dist/agent/personaMatcher.d.ts.map +0 -1
- package/dist/agent/personaMatcher.js +0 -60
- package/dist/agent/personaMatcher.js.map +0 -1
- package/dist/agent/userFactExtractor.d.ts +0 -25
- package/dist/agent/userFactExtractor.d.ts.map +0 -1
- package/dist/agent/userFactExtractor.js +0 -81
- package/dist/agent/userFactExtractor.js.map +0 -1
- package/dist/eval/evalRunner.d.ts +0 -94
- package/dist/eval/evalRunner.d.ts.map +0 -1
- package/dist/eval/evalRunner.js +0 -141
- package/dist/eval/evalRunner.js.map +0 -1
- package/dist/eval/evalTypes.d.ts +0 -148
- package/dist/eval/evalTypes.d.ts.map +0 -1
- package/dist/eval/evalTypes.js +0 -118
- package/dist/eval/evalTypes.js.map +0 -1
- package/dist/eval/scenarios.d.ts +0 -33
- package/dist/eval/scenarios.d.ts.map +0 -1
- package/dist/eval/scenarios.js +0 -109
- package/dist/eval/scenarios.js.map +0 -1
- package/dist/llm/embedding.d.ts +0 -67
- package/dist/llm/embedding.d.ts.map +0 -1
- package/dist/llm/embedding.js +0 -184
- package/dist/llm/embedding.js.map +0 -1
- package/dist/memory/hybridMerge.d.ts +0 -54
- package/dist/memory/hybridMerge.d.ts.map +0 -1
- package/dist/memory/hybridMerge.js +0 -36
- package/dist/memory/hybridMerge.js.map +0 -1
- package/dist/memory/inMemoryRelationStore.d.ts +0 -51
- package/dist/memory/inMemoryRelationStore.d.ts.map +0 -1
- package/dist/memory/inMemoryRelationStore.js +0 -65
- package/dist/memory/inMemoryRelationStore.js.map +0 -1
- package/dist/memory/loader.d.ts +0 -49
- package/dist/memory/loader.d.ts.map +0 -1
- package/dist/memory/loader.js +0 -93
- package/dist/memory/loader.js.map +0 -1
- package/dist/memory/recall.d.ts +0 -66
- package/dist/memory/recall.d.ts.map +0 -1
- package/dist/memory/recall.js +0 -142
- package/dist/memory/recall.js.map +0 -1
- package/dist/memory/relationStore.d.ts +0 -78
- package/dist/memory/relationStore.d.ts.map +0 -1
- package/dist/memory/relationStore.js +0 -2
- package/dist/memory/relationStore.js.map +0 -1
- package/dist/memory/store.d.ts +0 -50
- package/dist/memory/store.d.ts.map +0 -1
- package/dist/memory/store.js +0 -161
- package/dist/memory/store.js.map +0 -1
- package/dist/memory/userProfile.d.ts +0 -156
- package/dist/memory/userProfile.d.ts.map +0 -1
- package/dist/memory/userProfile.js +0 -320
- package/dist/memory/userProfile.js.map +0 -1
- package/dist/memory/vectorStore.d.ts +0 -185
- package/dist/memory/vectorStore.d.ts.map +0 -1
- package/dist/memory/vectorStore.js +0 -257
- package/dist/memory/vectorStore.js.map +0 -1
- package/dist/persona/personaManager.d.ts +0 -160
- package/dist/persona/personaManager.d.ts.map +0 -1
- package/dist/persona/personaManager.js +0 -414
- package/dist/persona/personaManager.js.map +0 -1
- package/dist/persona/types.d.ts +0 -32
- package/dist/persona/types.d.ts.map +0 -1
- package/dist/persona/types.js +0 -5
|
@@ -1,58 +1,157 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* 负责会话切换、分叉、恢复和消息加载,通过回调函数访问 Agent 的当前组件状态,
|
|
6
|
-
* 避免与 Agent 的 history/loop 引用生命周期耦合。
|
|
7
|
-
*
|
|
8
|
-
* Agent 通过组合方式持有 SessionManager 实例,将会话相关操作委托给它。
|
|
2
|
+
* 会话管理器:从 Agent 拆出的独立会话职责,负责会话切换/分叉/恢复/消息加载。
|
|
3
|
+
* 通过回调访问 Agent 当前组件状态,避免与 history/loop 引用生命周期耦合。
|
|
4
|
+
* 不中断工作模型:SessionCheckpoint 为同进程内存态快照(不落盘、无序列化/恢复路径);SessionStateMachine 管理三态流转。
|
|
9
5
|
*/
|
|
10
6
|
import { logger } from '../../logging/logger.js';
|
|
11
|
-
import { chatBusyError } from '../../utils/errors.js';
|
|
12
|
-
//
|
|
13
|
-
import {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
*/
|
|
7
|
+
import { chatBusyError, configError } from '../../utils/errors.js';
|
|
8
|
+
// 会话标识格式契约(SSOT):恢复最近会话时把 sessionId 拆成 (date, session) 供 loadMessages
|
|
9
|
+
import { isValidSessionId, splitSessionId } from '../../utils/time.js';
|
|
10
|
+
import { AGENT_CONSTANTS } from '../../agent/constants.js';
|
|
11
|
+
import { GoalConsistencyChecker } from '../../agent/managers/goalConsistencyChecker.js';
|
|
12
|
+
import { SessionStateMachine } from '../../agent/sessionStateMachine.js';
|
|
13
|
+
/** 会话管理器:经回调访问 Agent 当前组件状态,支持 Agent 重建后自动取最新引用 */
|
|
19
14
|
export class SessionManager {
|
|
20
|
-
/** 获取当前 MessageHistory 实例的回调 */
|
|
21
15
|
getHistory;
|
|
22
|
-
/** 获取当前 AgentLoop 实例的回调 */
|
|
23
16
|
getLoop;
|
|
24
17
|
/** 会话存储(可选,由宿主注入) */
|
|
25
18
|
sessionStore;
|
|
26
|
-
/** 检查对话是否繁忙的回调 */
|
|
27
19
|
isChatBusy;
|
|
28
20
|
/** 发射事件(委托给 Agent 的 TypedEventEmitter) */
|
|
29
21
|
emitEvent;
|
|
22
|
+
// ─── 不中断工作模型(检查点 + 状态机) ─────────────────
|
|
23
|
+
/** 会话状态机(三态流转,经有界代理方法访问) */
|
|
24
|
+
stateMachine;
|
|
25
|
+
/** 当前会话检查点(运行时状态快照) */
|
|
26
|
+
checkpoint = null;
|
|
27
|
+
/** 检查点脏标记(内存态收口):touchCheckpoint 置位、settleCheckpoint 清理(纯内存态,不落盘) */
|
|
28
|
+
checkpointDirty = false;
|
|
29
|
+
/** 目标一致性校验器(目标版本一致性校验) */
|
|
30
|
+
consistencyChecker;
|
|
31
|
+
/**
|
|
32
|
+
* 连续暂停时间戳数组(防滥用窗口):每次高风险暂停记录时间戳,窗口(1 小时)内连续 2 次则强制降为低风险暂停。
|
|
33
|
+
* ⚠ 进程内状态,不入 SessionCheckpoint——重启归零是设计(反滥用卫生状态,非执行状态);1h 窗口自愈防线,勿引入持久化。
|
|
34
|
+
*/
|
|
35
|
+
consecutivePauseTimestamps = [];
|
|
36
|
+
/** 连续暂停时间窗口(毫秒),1 小时前的暂停不计入连续计数 */
|
|
37
|
+
static CONSECUTIVE_PAUSE_WINDOW_MS = 3_600_000;
|
|
38
|
+
// ── 运行时暂停超时检测 ────────────────────────────────
|
|
39
|
+
/** 暂停超时检测定时器(只在 paused 状态时运行) */
|
|
40
|
+
_pauseTimeoutTimer = null;
|
|
41
|
+
/** 暂停超时检测间隔(毫秒),30 秒检查一次心跳 */
|
|
42
|
+
static PAUSE_TIMEOUT_CHECK_INTERVAL = 30_000;
|
|
30
43
|
constructor(getHistory, getLoop, sessionStore, isChatBusy, emitEvent) {
|
|
31
44
|
this.getHistory = getHistory;
|
|
32
45
|
this.getLoop = getLoop;
|
|
33
46
|
this.sessionStore = sessionStore;
|
|
34
47
|
this.isChatBusy = isChatBusy;
|
|
35
48
|
this.emitEvent = emitEvent;
|
|
49
|
+
this.stateMachine = new SessionStateMachine('running');
|
|
50
|
+
this.consistencyChecker = new GoalConsistencyChecker();
|
|
51
|
+
}
|
|
52
|
+
// ─── SSOT 状态机代理(封装 stateMachine,暴露有界接口) ───
|
|
53
|
+
/** 会话状态(状态机状态) */
|
|
54
|
+
get status() {
|
|
55
|
+
return this.stateMachine.status;
|
|
56
|
+
}
|
|
57
|
+
/** 获取暂停信息(仅 PAUSED 时有效) */
|
|
58
|
+
get pauseInfo() {
|
|
59
|
+
return this.stateMachine.pauseInfo;
|
|
60
|
+
}
|
|
61
|
+
/** 获取异常原因(仅 ERROR 时有效) */
|
|
62
|
+
get errorInfo() {
|
|
63
|
+
return this.stateMachine.errorInfo;
|
|
64
|
+
}
|
|
65
|
+
/** 获取待处理暂停信息(只读,不消费) */
|
|
66
|
+
get pendingPauseInfo() {
|
|
67
|
+
return this.stateMachine.pendingPauseInfo;
|
|
68
|
+
}
|
|
69
|
+
/** 检查是否处于暂停态 */
|
|
70
|
+
isPaused() {
|
|
71
|
+
return this.stateMachine.status === 'paused';
|
|
72
|
+
}
|
|
73
|
+
/** 检查是否处于运行态 */
|
|
74
|
+
isRunning() {
|
|
75
|
+
return this.stateMachine.status === 'running';
|
|
76
|
+
}
|
|
77
|
+
/** 待处理暂停请求是否在等待中 */
|
|
78
|
+
isPausePending() {
|
|
79
|
+
return this.stateMachine.isPausePending();
|
|
36
80
|
}
|
|
37
81
|
/**
|
|
38
|
-
*
|
|
82
|
+
* 请求软暂停(仅 RUNNING 状态允许)
|
|
39
83
|
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
84
|
+
* 暂存暂停原因和来源,待 loop 边界真正挂起时由 consumePendingPause() 消费。
|
|
85
|
+
* 与 pause() 不同:pause() 立即翻状态机,requestPause() 仅注册待处理请求。
|
|
86
|
+
*/
|
|
87
|
+
requestPause(reason, source = 'user') {
|
|
88
|
+
return this.stateMachine.requestPause(reason, source);
|
|
89
|
+
}
|
|
90
|
+
/** 消费待处理暂停请求(在 loop 边界真正挂起时调用) */
|
|
91
|
+
consumePendingPause() {
|
|
92
|
+
return this.stateMachine.consumePendingPause();
|
|
93
|
+
}
|
|
94
|
+
/** 取消待处理暂停请求 */
|
|
95
|
+
cancelPendingPause() {
|
|
96
|
+
this.stateMachine.cancelPendingPause();
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* 会话替换 chokepoint:作废所有会话级派生缓存。
|
|
100
|
+
* 任何替换 loop 工作记忆(switch/fork/restore)入口都必须经此,新增派生缓存在此注册一处避免"对称的另一半没写完"。
|
|
42
101
|
*/
|
|
102
|
+
invalidateSessionDerivedState() {
|
|
103
|
+
// 真实 loop 必有 resetContextSummary;测试 mock 可能未实现,缺失时 no-op
|
|
104
|
+
this.getLoop()?.resetContextSummary?.();
|
|
105
|
+
}
|
|
106
|
+
/** 切换当前会话。底层 MessageHistory.switchSession 为纯同步操作,不引入无意义 async 包装。 */
|
|
43
107
|
switchSession(newSession) {
|
|
44
|
-
//
|
|
108
|
+
// 对话进行中切换会导致消息持久化分散
|
|
45
109
|
if (this.isChatBusy()) {
|
|
46
110
|
throw chatBusyError('切换会话');
|
|
47
111
|
}
|
|
48
|
-
|
|
112
|
+
// 暂停/错误态切换语义未定义,保守拒绝(与 fork/restore 繁忙守卫同构)
|
|
113
|
+
if (this.stateMachine.status !== 'running') {
|
|
114
|
+
throw configError('切换会话', `会话处于 ${this.stateMachine.status} 态,无法切换`, [
|
|
115
|
+
'仅 running 态允许 switchSession',
|
|
116
|
+
]);
|
|
117
|
+
}
|
|
118
|
+
// 检查点是当前会话的工作状态,切换时清空内存态(检查点纯内存态,不落盘)
|
|
119
|
+
if (this.checkpoint && this.checkpoint.sessionId !== newSession) {
|
|
120
|
+
this.settleCheckpoint(true);
|
|
121
|
+
this.checkpoint = null;
|
|
122
|
+
this.checkpointDirty = false;
|
|
123
|
+
}
|
|
124
|
+
const result = this.getHistory().switchSession(newSession);
|
|
125
|
+
// 会话已替换:作废上下文摘要等派生缓存,避免陈旧摘要注入新会话
|
|
126
|
+
this.invalidateSessionDerivedState();
|
|
127
|
+
return result;
|
|
49
128
|
}
|
|
50
129
|
/**
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
130
|
+
* 打开指定会话(新建空/切换已有)= switchSession 的完整版:切换身份后同步 loadSessionMessages + restoreHistory。
|
|
131
|
+
* 已有会话恢复历史到工作记忆;新建空会话 restoreHistory([]) 清空(保留 system prompt),避免旧会话上下文残留注入首条消息。
|
|
132
|
+
* 使用正则 /^(\d{4}-\d{2}-\d{2})-(.+)$/ 解析,正确处理包含连字符的会话名(如 2024-01-15-my-session)。
|
|
133
|
+
* @returns 恢复的消息数(0 表示新建空会话)
|
|
55
134
|
*/
|
|
135
|
+
async switchToSession(sessionId) {
|
|
136
|
+
// 对话进行中切换会话会导致消息持久化分散
|
|
137
|
+
if (this.isChatBusy()) {
|
|
138
|
+
throw chatBusyError('打开会话');
|
|
139
|
+
}
|
|
140
|
+
// 严格校验会话标识格式(SSOT:isValidSessionId 单一真源)+ 拆解(date/session,会话名可含连字符)
|
|
141
|
+
if (!isValidSessionId(sessionId)) {
|
|
142
|
+
throw configError('打开会话', `会话标识格式错误:${sessionId}`, [
|
|
143
|
+
'格式应为 YYYY-MM-DD-sessionName',
|
|
144
|
+
]);
|
|
145
|
+
}
|
|
146
|
+
const { date, session } = splitSessionId(sessionId);
|
|
147
|
+
// 切换会话身份(内部含 busy/状态守卫 + 作废派生缓存)
|
|
148
|
+
this.switchSession(session);
|
|
149
|
+
// 加载/清空工作记忆:空会话 → restoreHistory([]) 清空(缓存已作废,无陈旧注入)
|
|
150
|
+
const messages = await this.getHistory().loadSessionMessages(date, session);
|
|
151
|
+
this.applySessionToLoop(messages);
|
|
152
|
+
return messages.length;
|
|
153
|
+
}
|
|
154
|
+
/** 获取当前会话的日期和会话名(供 switchSession 前触发 content 类归档);history 未初始化返回 null */
|
|
56
155
|
getCurrentSessionInfo() {
|
|
57
156
|
try {
|
|
58
157
|
const history = this.getHistory();
|
|
@@ -67,51 +166,67 @@ export class SessionManager {
|
|
|
67
166
|
return null;
|
|
68
167
|
}
|
|
69
168
|
}
|
|
169
|
+
/** 读取会话标题元数据:供宿主历史列表展示;未注入 getSessionMeta 时标题层静默失效 */
|
|
170
|
+
getSessionMeta(sessionId) {
|
|
171
|
+
return this.sessionStore?.getSessionMeta?.(sessionId);
|
|
172
|
+
}
|
|
173
|
+
/** 手动改名会话:仅写 displayName(用户可改),不改 autoName(LLM 只读)。显示回退:displayName 非空用之,否则用 autoName */
|
|
174
|
+
renameSession(sessionId, title) {
|
|
175
|
+
this.sessionStore?.updateSessionMeta?.(sessionId, { displayName: title });
|
|
176
|
+
}
|
|
70
177
|
/**
|
|
71
|
-
*
|
|
178
|
+
* 从指定 Round 位置分叉当前会话(round-based 模式唯一分叉方式)。
|
|
72
179
|
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
* -
|
|
76
|
-
* - 记忆索引(IMemoryStorage)全局共享,不受分叉影响
|
|
180
|
+
* 分叉后:新会话包含分叉点及之前的所有 Round ID,是完全平等的普通会话。
|
|
181
|
+
* 原会话完整保留可切回;新会话的问答闭环独立互不干扰。
|
|
182
|
+
* ⚠ fork 不隔离记忆——分支 A 的 round-summary 会在分支 B 召回中出现。
|
|
77
183
|
*
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
* 应创建独立 Agent 实例 + 独立 dataDir,而非 fork。
|
|
81
|
-
*
|
|
82
|
-
* @param targetSession - 自定义新分支名(可选,不传则自动生成)
|
|
83
|
-
* @returns { newSession, messageCount }
|
|
184
|
+
* @param roundId - 分叉点的 Round ID(可选;不传默认使用最后一个 Round)
|
|
185
|
+
* @param targetSession - 可选,自定义新会话名;不传则自动生成
|
|
84
186
|
*/
|
|
85
|
-
forkSession(targetSession) {
|
|
187
|
+
forkSession(roundId, targetSession) {
|
|
86
188
|
if (this.isChatBusy()) {
|
|
87
189
|
throw chatBusyError('分叉');
|
|
88
190
|
}
|
|
89
191
|
const history = this.getHistory();
|
|
90
|
-
//
|
|
192
|
+
// 记录源会话名(含日期前缀完整名,用于事件)
|
|
91
193
|
const sourceSessionName = history.currentSessionName;
|
|
92
|
-
// 委托 MessageHistory
|
|
93
|
-
const result = history.forkSession(targetSession);
|
|
94
|
-
//
|
|
95
|
-
|
|
96
|
-
|
|
194
|
+
// 委托 MessageHistory 完成分叉(round-based:复制 Round ID 列表)
|
|
195
|
+
const result = history.forkSession(roundId, targetSession);
|
|
196
|
+
// 将新会话的问答闭环加载到 AgentLoop 工作记忆
|
|
197
|
+
// round-based 模式:通过 roundIds 加载消息并应用到 loop
|
|
198
|
+
const sessionMessages = history.loadRoundBasedMessages(result.roundIds);
|
|
199
|
+
this.applySessionToLoop(sessionMessages);
|
|
200
|
+
// 关键约束:分叉后清空 checkpoint,防止新分支的 plan/goal 写入源会话检查点。
|
|
201
|
+
if (this.checkpoint) {
|
|
202
|
+
this.settleCheckpoint(true);
|
|
203
|
+
this.checkpoint = null;
|
|
204
|
+
this.checkpointDirty = false;
|
|
205
|
+
}
|
|
97
206
|
this.emitEvent('sessionForked', {
|
|
98
207
|
from: sourceSessionName,
|
|
99
208
|
to: `${result.date}-${result.newSession}`,
|
|
100
|
-
|
|
209
|
+
roundCount: result.roundIds.length,
|
|
101
210
|
});
|
|
102
211
|
return {
|
|
103
212
|
newSession: result.newSession,
|
|
104
|
-
|
|
213
|
+
// 透传分叉创建的会话日期(供分叉会话命名等场景按完整会话键定位)
|
|
214
|
+
date: result.date,
|
|
215
|
+
roundCount: result.roundIds.length,
|
|
216
|
+
roundIds: result.roundIds,
|
|
105
217
|
};
|
|
106
218
|
}
|
|
107
219
|
/**
|
|
108
|
-
*
|
|
220
|
+
* 恢复最近活跃会话:经 ISessionStore 加载最近消息到工作记忆;宿主未注入则返回 0。
|
|
221
|
+
*
|
|
222
|
+
* 会话管理纯度:会话一律手动创建(宿主标题条「+」唯一入口),
|
|
223
|
+
* 本方法只做「恢复」、绝不隐式创建会话——无按天归档隐式语义
|
|
224
|
+
* (「今天-main 优先」类规则),也无 preferredSession 参数。最近活跃的唯一时间序真理源 = listSessionMetas[0](updatedAt 降序)。
|
|
109
225
|
*
|
|
110
|
-
*
|
|
111
|
-
* 宿主项目需注入 ISessionStore 实现,否则返回 0。
|
|
226
|
+
* @returns 恢复的消息条数(0 = 无可恢复会话)
|
|
112
227
|
*/
|
|
113
|
-
async restoreMostRecentSession(
|
|
114
|
-
//
|
|
228
|
+
async restoreMostRecentSession() {
|
|
229
|
+
// 对话进行中恢复会导致 loop 工作记忆被替换
|
|
115
230
|
if (this.isChatBusy()) {
|
|
116
231
|
throw chatBusyError('恢复会话');
|
|
117
232
|
}
|
|
@@ -119,29 +234,20 @@ export class SessionManager {
|
|
|
119
234
|
logger.debug({ hasSessionStore: false }, '未注入 ISessionStore,无法恢复会话');
|
|
120
235
|
return 0;
|
|
121
236
|
}
|
|
122
|
-
//
|
|
123
|
-
const
|
|
124
|
-
|
|
125
|
-
|
|
237
|
+
// SSOT:listSessionMetas 依 updatedAt 降序,[0] 即最近活跃会话(唯一时间序真理源)
|
|
238
|
+
const metas = this.sessionStore.listSessionMetas();
|
|
239
|
+
const target = metas[0]?.sessionId;
|
|
240
|
+
if (!target) {
|
|
241
|
+
logger.debug({ sessionCount: metas.length }, '没有可恢复的历史会话');
|
|
126
242
|
return 0;
|
|
127
243
|
}
|
|
128
|
-
//
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
// 解析 "YYYY-MM-DD-session" 格式
|
|
133
|
-
const match = (preferred ?? '').match(/^(\d{4}-\d{2}-\d{2})-(.+)$/);
|
|
134
|
-
if (!match) {
|
|
135
|
-
logger.debug({ session: preferred }, '会话标识格式不匹配');
|
|
244
|
+
// 解析 "YYYY-MM-DD-session"(sessionId 格式契约真理源 = splitSessionId,session 名可含连字符)
|
|
245
|
+
const parsed = splitSessionId(target);
|
|
246
|
+
if (!parsed.session) {
|
|
247
|
+
logger.debug({ session: target }, '会话标识格式不匹配');
|
|
136
248
|
return 0;
|
|
137
249
|
}
|
|
138
|
-
const
|
|
139
|
-
// match 已经保证 date 和 session 存在(正则匹配两个捕获组),但 TypeScript 类型收窄有限
|
|
140
|
-
if (!date || !session) {
|
|
141
|
-
logger.debug({ session: preferred }, '会话标识解析失败');
|
|
142
|
-
return 0;
|
|
143
|
-
}
|
|
144
|
-
const sessionMessages = this.sessionStore.loadMessages(date, session);
|
|
250
|
+
const sessionMessages = this.sessionStore.loadMessages(parsed.date, parsed.session);
|
|
145
251
|
if (sessionMessages.length === 0) {
|
|
146
252
|
logger.debug({ messageCount: 0 }, '没有找到可恢复的历史会话');
|
|
147
253
|
return 0;
|
|
@@ -149,11 +255,9 @@ export class SessionManager {
|
|
|
149
255
|
this.applySessionToLoop(sessionMessages);
|
|
150
256
|
return sessionMessages.length;
|
|
151
257
|
}
|
|
152
|
-
/**
|
|
153
|
-
* 恢复指定会话的对话
|
|
154
|
-
*/
|
|
258
|
+
/** 恢复指定会话的对话 */
|
|
155
259
|
async restoreSession(date, session) {
|
|
156
|
-
//
|
|
260
|
+
// 对话进行中恢复会导致 loop 工作记忆被替换
|
|
157
261
|
if (this.isChatBusy()) {
|
|
158
262
|
throw chatBusyError('恢复会话');
|
|
159
263
|
}
|
|
@@ -164,28 +268,685 @@ export class SessionManager {
|
|
|
164
268
|
this.applySessionToLoop(sessionMessages);
|
|
165
269
|
return sessionMessages.length;
|
|
166
270
|
}
|
|
167
|
-
/**
|
|
168
|
-
* 对外暴露的 loadSessionMessages 委托
|
|
169
|
-
*
|
|
170
|
-
* **注意:此方法会切换当前会话**(更新 currentDate/currentSession)。
|
|
171
|
-
* 加载后,下一次 chat() 的消息会写入被加载的会话。
|
|
172
|
-
*/
|
|
271
|
+
/** 对外暴露的 loadSessionMessages 委托。⚠ 会切换当前会话(更新 currentDate/currentSession),加载后下一条 chat 写入被加载会话 */
|
|
173
272
|
async loadSessionMessages(date, session) {
|
|
174
|
-
//
|
|
273
|
+
// 对话进行中加载会切换 currentDate/currentSession,导致消息持久化分散
|
|
175
274
|
if (this.isChatBusy()) {
|
|
176
275
|
throw chatBusyError('加载会话');
|
|
177
276
|
}
|
|
178
277
|
return this.getHistory().loadSessionMessages(date, session);
|
|
179
278
|
}
|
|
180
|
-
/**
|
|
181
|
-
* 将会话消息恢复到 AgentLoop 工作记忆
|
|
182
|
-
*/
|
|
279
|
+
/** 将会话消息恢复到 AgentLoop 工作记忆 */
|
|
183
280
|
applySessionToLoop(sessionMessages) {
|
|
184
281
|
const messages = sessionMessages.map((tm) => ({
|
|
185
282
|
role: tm.role,
|
|
186
283
|
content: tm.content,
|
|
187
284
|
}));
|
|
188
285
|
this.getLoop().restoreHistory(messages);
|
|
286
|
+
// 消息集合整体替换:作废派生缓存(与 switchSession 共用同一 chokepoint)
|
|
287
|
+
this.invalidateSessionDerivedState();
|
|
288
|
+
}
|
|
289
|
+
// ─── 不中断工作模型:检查点 + 状态机 ──────────────────
|
|
290
|
+
/**
|
|
291
|
+
* 检查点必需字段默认值工厂(默认值单一真理源)。
|
|
292
|
+
* 仅 createCheckpoint 新建/合并时使用——反序列化采用严格模式,不做静默补齐。
|
|
293
|
+
* 必须返回新实例:数组/对象默认值共享引用会让不同检查点互相污染(一个 push 计划任务项另一凭空多出)。
|
|
294
|
+
*/
|
|
295
|
+
static checkpointDefaults() {
|
|
296
|
+
return {
|
|
297
|
+
mainGoal: '',
|
|
298
|
+
currentGoal: '',
|
|
299
|
+
goalChangeSeq: 0,
|
|
300
|
+
plan: [],
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
/** 从当前运行时状态创建检查点:快照消息历史与会话标识生成 SessionCheckpoint(纯内存态,
|
|
304
|
+
* 不落盘——中止/断电走中断轮补全,运行时暂停同 turn 内存续跑) */
|
|
305
|
+
createCheckpoint(mainGoal) {
|
|
306
|
+
const history = this.getHistory();
|
|
307
|
+
const prev = this.checkpoint;
|
|
308
|
+
const defaults = SessionManager.checkpointDefaults();
|
|
309
|
+
// 合并语义:以已有检查点为基底展开,仅覆写本次重算的字段。
|
|
310
|
+
// 【禁止改回对象字面量整体重建】——整体重建≈隐式字段白名单,任何未显式列出的字段每次 pause 被静默丢弃
|
|
311
|
+
// (整体重建会使 planItemLog/completedToolCalls/pauseMeta 静默归零,非幂等工具恢复后重复执行)
|
|
312
|
+
this.checkpoint = {
|
|
313
|
+
...(prev ?? {}),
|
|
314
|
+
// 以下字段由本次快照重算,覆写基底
|
|
315
|
+
sessionId: history.currentSessionName,
|
|
316
|
+
// 状态真理源是状态机,检查点只是其投影
|
|
317
|
+
status: this.stateMachine.status,
|
|
318
|
+
// error 以状态机为准:状态机无异常即已脱离异常态,清除旧记录
|
|
319
|
+
error: this.stateMachine.errorInfo
|
|
320
|
+
? {
|
|
321
|
+
cause: this.stateMachine.errorInfo,
|
|
322
|
+
at: Date.now(),
|
|
323
|
+
recovered: false,
|
|
324
|
+
}
|
|
325
|
+
: undefined,
|
|
326
|
+
mainGoal: mainGoal ?? prev?.mainGoal ?? defaults.mainGoal,
|
|
327
|
+
// 不传 mainGoal 时保留已有 currentGoal(防 pause() 内调用覆盖 updateGoal 的更新)
|
|
328
|
+
currentGoal: mainGoal ?? prev?.currentGoal ?? prev?.mainGoal ?? defaults.currentGoal,
|
|
329
|
+
goalChangeSeq: prev?.goalChangeSeq ?? defaults.goalChangeSeq,
|
|
330
|
+
plan: prev?.plan ?? defaults.plan,
|
|
331
|
+
lastHeartbeat: Date.now(),
|
|
332
|
+
};
|
|
333
|
+
// 检查点内容已整体重算,强制清脏(纯内存,无落盘)
|
|
334
|
+
this.settleCheckpoint(true);
|
|
335
|
+
return this.checkpoint;
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* 标记检查点已变更(内存写点统一入口)。刷新心跳并置脏标记。
|
|
339
|
+
* 所有修改 this.checkpoint 的方法都必须调用本方法,不得直接赋值 lastHeartbeat——否则变更不被后续清脏感知,内存与清脏链路静默分叉。
|
|
340
|
+
*/
|
|
341
|
+
touchCheckpoint() {
|
|
342
|
+
if (!this.checkpoint)
|
|
343
|
+
return;
|
|
344
|
+
this.checkpoint.lastHeartbeat = Date.now(); // 心跳唯一写点
|
|
345
|
+
this.checkpointDirty = true;
|
|
346
|
+
}
|
|
347
|
+
/**
|
|
348
|
+
* 检查点脏标记清理(**纯内存态**):
|
|
349
|
+
* `SessionCheckpoint` 不落盘——中止/断电走「中断轮补全为完整 turn」而非跨重启恢复,
|
|
350
|
+
* 运行时暂停是同 turn 内续跑(内存态),无跨进程载体需求。故本方法仅清理脏标记,
|
|
351
|
+
* 不再写存储层(保留方法语义边界,避免为纯净化波及 40+ 处调用点)。
|
|
352
|
+
*/
|
|
353
|
+
settleCheckpoint(force = false) {
|
|
354
|
+
if (!this.checkpoint)
|
|
355
|
+
return;
|
|
356
|
+
if (!force && !this.checkpointDirty)
|
|
357
|
+
return;
|
|
358
|
+
// 纯内存:无落盘动作,仅清脏
|
|
359
|
+
this.checkpointDirty = false;
|
|
360
|
+
}
|
|
361
|
+
/** 获取当前检查点快照;未创建返回 null */
|
|
362
|
+
getCheckpoint() {
|
|
363
|
+
return this.checkpoint;
|
|
364
|
+
}
|
|
365
|
+
/**
|
|
366
|
+
* 暂停会话(用户/Agent/系统均可触发)。暂停前自动创建检查点;高风险暂停计入连续计数(防滥用),低风险不计。
|
|
367
|
+
* @param lowRisk 低风险暂停不计连续计数(默认 false)
|
|
368
|
+
*/
|
|
369
|
+
pause(reason, source = 'user', lowRisk = false) {
|
|
370
|
+
const result = this.stateMachine.pause(reason, source);
|
|
371
|
+
if (result.allowed) {
|
|
372
|
+
// 先在 createCheckpoint 前记 pausedAt,检查点经 spread 继承,省去 pause() 内的二次补设分支
|
|
373
|
+
if (this.checkpoint) {
|
|
374
|
+
this.checkpoint.pausedAt = Date.now();
|
|
375
|
+
}
|
|
376
|
+
this.createCheckpoint();
|
|
377
|
+
// 首轮暂停无现有检查点,pausedAt 未被 spread 继承,补设并标脏
|
|
378
|
+
if (this.checkpoint && !this.checkpoint.pausedAt) {
|
|
379
|
+
this.checkpoint.pausedAt = Date.now();
|
|
380
|
+
this.touchCheckpoint();
|
|
381
|
+
}
|
|
382
|
+
// 仅高风险暂停记录时间戳(低风险由 Agent 自动兜底不累积)
|
|
383
|
+
if (!lowRisk) {
|
|
384
|
+
this.consecutivePauseTimestamps.push(Date.now());
|
|
385
|
+
logger.debug({ consecutivePauseCount: this.consecutivePauseTimestamps.length, reason }, '高风险暂停已记录时间戳');
|
|
386
|
+
}
|
|
387
|
+
else {
|
|
388
|
+
logger.debug({ reason }, '低风险暂停不记录时间戳(Agent 自动兜底)');
|
|
389
|
+
}
|
|
390
|
+
// 启动暂停超时检测,防暂停会话长时间占用资源
|
|
391
|
+
this.startPauseTimeoutTimer();
|
|
392
|
+
this.emitEvent('sessionPaused', {
|
|
393
|
+
reason,
|
|
394
|
+
source,
|
|
395
|
+
sessionId: this.checkpoint?.sessionId,
|
|
396
|
+
});
|
|
397
|
+
logger.info({ reason, source, lowRisk }, '会话已暂停');
|
|
398
|
+
}
|
|
399
|
+
return result.allowed;
|
|
400
|
+
}
|
|
401
|
+
/** 恢复会话:从 PAUSED 恢复到 RUNNING。超时会话需重新开始,不能恢复 */
|
|
402
|
+
resume() {
|
|
403
|
+
// 暂停超时阻止恢复
|
|
404
|
+
if (this.checkpoint && this.isPauseTimedOut(this.checkpoint)) {
|
|
405
|
+
logger.warn({ sessionId: this.checkpoint.sessionId }, '暂停超时,无法恢复会话(请重新开始)');
|
|
406
|
+
this.emitEvent('sessionResumeBlocked', {
|
|
407
|
+
sessionId: this.checkpoint.sessionId,
|
|
408
|
+
reason: 'pause_timed_out',
|
|
409
|
+
});
|
|
410
|
+
return false;
|
|
411
|
+
}
|
|
412
|
+
const result = this.stateMachine.resume();
|
|
413
|
+
if (result.allowed) {
|
|
414
|
+
this.stopPauseTimeoutTimer();
|
|
415
|
+
if (this.checkpoint) {
|
|
416
|
+
// 从状态机投影 status(SSOT),避免写死
|
|
417
|
+
this.checkpoint.status = this.stateMachine.status;
|
|
418
|
+
// 收口暂停态残留:pausedAt 是「暂停起点」标记,恢复即应卸载——
|
|
419
|
+
// 否则检查点长期残留 pausedAt(status=running + pausedAt 并列的脏快照),
|
|
420
|
+
// 且下一次暂停超时判定会被旧起点污染。随 setPauseMeta 一并更新(纯内存)。
|
|
421
|
+
delete this.checkpoint.pausedAt;
|
|
422
|
+
// 卸载 pauseMeta 状态层挂载物(仅含展示信息,恢复即"回到运行"应卸载,否则任务面板残留"继续"按钮)。
|
|
423
|
+
// 走 setPauseMeta 唯一写入口而非内联赋值,避免绕过其兜底链路(其内部已含 touch+settle,此处不再重复调用)
|
|
424
|
+
this.setPauseMeta(undefined);
|
|
425
|
+
}
|
|
426
|
+
// 连续暂停计数在暂停超时处理(checkPauseTimeout)对称重置,
|
|
427
|
+
// 不在 resume() 里清零——避免用户回答前过早清零导致防滥用机制无效
|
|
428
|
+
this.emitEvent('sessionResumed', {
|
|
429
|
+
sessionId: this.checkpoint?.sessionId,
|
|
430
|
+
});
|
|
431
|
+
logger.info('会话已恢复');
|
|
432
|
+
}
|
|
433
|
+
return result.allowed;
|
|
434
|
+
}
|
|
435
|
+
/** 触发异常(仅 RUNNING 态)。异常时自动创建检查点;createCheckpoint 会从状态机投影 status/error,不再手工赋值避免并列真理源 */
|
|
436
|
+
triggerError(cause) {
|
|
437
|
+
const result = this.stateMachine.triggerError(cause);
|
|
438
|
+
if (result.allowed) {
|
|
439
|
+
this.createCheckpoint();
|
|
440
|
+
this.emitEvent('sessionError', {
|
|
441
|
+
cause,
|
|
442
|
+
sessionId: this.checkpoint?.sessionId,
|
|
443
|
+
});
|
|
444
|
+
logger.warn({ cause }, '会话异常');
|
|
445
|
+
}
|
|
446
|
+
return result.allowed;
|
|
447
|
+
}
|
|
448
|
+
// ── 防滥用:连续暂停计数 ───────────────────────────────
|
|
449
|
+
/** 清理过期暂停时间戳(窗口过滤):移除超过窗口(1 小时)的旧时间戳,仅保留窗口内暂停;每次查询计数时自动调用 */
|
|
450
|
+
pruneStalePauseTimestamps() {
|
|
451
|
+
const cutoff = Date.now() - SessionManager.CONSECUTIVE_PAUSE_WINDOW_MS;
|
|
452
|
+
const before = this.consecutivePauseTimestamps.length;
|
|
453
|
+
this.consecutivePauseTimestamps = this.consecutivePauseTimestamps.filter((ts) => ts > cutoff);
|
|
454
|
+
const pruned = before - this.consecutivePauseTimestamps.length;
|
|
455
|
+
if (pruned > 0) {
|
|
456
|
+
logger.debug({ pruned, remaining: this.consecutivePauseTimestamps.length }, '过期暂停时间戳已清理');
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
/** 检查连续暂停是否已达上限(防滥用):先清理窗口外时间戳,再查窗口内暂停≥2 次则强制降为低风险暂停。低风险不计数 */
|
|
460
|
+
isPauseLimitReached() {
|
|
461
|
+
this.pruneStalePauseTimestamps();
|
|
462
|
+
return this.consecutivePauseTimestamps.length >= 2;
|
|
463
|
+
}
|
|
464
|
+
/** 获取当前连续暂停计数:先清理窗口外时间戳,返回窗口内有效暂停数 */
|
|
465
|
+
getConsecutivePauseCount() {
|
|
466
|
+
this.pruneStalePauseTimestamps();
|
|
467
|
+
return this.consecutivePauseTimestamps.length;
|
|
468
|
+
}
|
|
469
|
+
/** 重置连续暂停计数(防滥用):暂停超时处理后调用。与 resume() 分离,防回答前过早清零致防滥用失效 */
|
|
470
|
+
resetConsecutivePauseCount() {
|
|
471
|
+
this.consecutivePauseTimestamps = [];
|
|
472
|
+
logger.debug('连续暂停时间戳已清空');
|
|
473
|
+
}
|
|
474
|
+
/**
|
|
475
|
+
* 放弃当前暂停检查点(宿主 stop 语义的内核出口)
|
|
476
|
+
*
|
|
477
|
+
* 语义 = 用户决定**彻底放弃**暂停执行,不再 resume。清理内容:
|
|
478
|
+
* 1. 停暂停超时定时器(防止已放弃的 checkpoint 继续被超时清理)
|
|
479
|
+
* 2. 清内存态 checkpoint + checkpointDirty + pauseMeta
|
|
480
|
+
* 3. 状态机 resetToRunning(回到 idle 之前的 running 基础态,下次 chat() 正常)
|
|
481
|
+
*
|
|
482
|
+
* 与 resume() 的区别:resume = 想继续跑暂停点;discardCheckpoint = 不想了,检查点作废。
|
|
483
|
+
* 与 pause() 的对称:pause 创建检查点;discardCheckpoint 销毁检查点。
|
|
484
|
+
* @returns true=成功清理;false=无暂停检查点(空闲态调了个空)
|
|
485
|
+
*/
|
|
486
|
+
discardCheckpoint() {
|
|
487
|
+
if (!this.checkpoint) {
|
|
488
|
+
logger.debug('discardCheckpoint:无检查点可清理(可能已清理或从未暂停)');
|
|
489
|
+
return false;
|
|
490
|
+
}
|
|
491
|
+
const sessionId = this.checkpoint.sessionId;
|
|
492
|
+
logger.info({ sessionId }, '放弃暂停检查点(用户 stop 语义)');
|
|
493
|
+
// 1. 停暂停超时定时器
|
|
494
|
+
this.stopPauseTimeoutTimer();
|
|
495
|
+
// 2. 清内存态(检查点纯内存态不落盘,无存储清理动作)
|
|
496
|
+
this.checkpoint = null;
|
|
497
|
+
this.checkpointDirty = false;
|
|
498
|
+
this.setPauseMeta(undefined);
|
|
499
|
+
// 3. 状态机回到 running
|
|
500
|
+
this.stateMachine.resetToRunning();
|
|
501
|
+
return true;
|
|
502
|
+
}
|
|
503
|
+
/** 从异常恢复:标记 error.recovered=true 并经 stateMachine.recover 校验。检查点不落盘,恢复仅活在内存态 */
|
|
504
|
+
recover() {
|
|
505
|
+
if (!this.checkpoint || !this.checkpoint.error) {
|
|
506
|
+
logger.warn('无法恢复:无检查点或异常信息');
|
|
507
|
+
return false;
|
|
508
|
+
}
|
|
509
|
+
this.checkpoint.error.recovered = true;
|
|
510
|
+
this.touchCheckpoint();
|
|
511
|
+
const result = this.stateMachine.recover(this.checkpoint);
|
|
512
|
+
if (result.allowed) {
|
|
513
|
+
if (this.checkpoint) {
|
|
514
|
+
// 从状态机投影 status(SSOT)
|
|
515
|
+
this.checkpoint.status = this.stateMachine.status;
|
|
516
|
+
this.touchCheckpoint();
|
|
517
|
+
}
|
|
518
|
+
// 内存态收口(无落盘:recovered 只活内存,跨进程一律走中断补全)
|
|
519
|
+
this.settleCheckpoint();
|
|
520
|
+
this.emitEvent('sessionRecovered', {
|
|
521
|
+
sessionId: this.checkpoint?.sessionId,
|
|
522
|
+
});
|
|
523
|
+
logger.info('会话已从异常恢复');
|
|
524
|
+
}
|
|
525
|
+
return result.allowed;
|
|
526
|
+
}
|
|
527
|
+
/**
|
|
528
|
+
* 更新检查点目标版本:递增 goalChangeSeq 触发漂移检测。漂移处置由内核自兜底不依赖宿主:
|
|
529
|
+
* drift 级内核直接低风险暂停强制确认;minor 级仅发射事件+日志不打断执行。
|
|
530
|
+
* goalDriftDetected 事件为可观测性出口,宿主可选订阅,不订阅不影响正确性。
|
|
531
|
+
*/
|
|
532
|
+
updateGoal(newGoal) {
|
|
533
|
+
if (!this.checkpoint) {
|
|
534
|
+
this.createCheckpoint(newGoal);
|
|
535
|
+
return null;
|
|
536
|
+
}
|
|
537
|
+
// 幂等短路:目标值未变直接返回(返回 level='same'),不递增 goalChangeSeq、不触发漂移。
|
|
538
|
+
// 续跑/补充输入每轮喂相同 currentGoal,若无短路 goalChangeSeq 会退化为轮次计数器
|
|
539
|
+
if (newGoal === this.checkpoint.currentGoal) {
|
|
540
|
+
return {
|
|
541
|
+
level: 'same',
|
|
542
|
+
similarity: 1,
|
|
543
|
+
constraints: this.consistencyChecker.extractConstraints(this.checkpoint.mainGoal),
|
|
544
|
+
constraintsConsistent: true,
|
|
545
|
+
};
|
|
546
|
+
}
|
|
547
|
+
// 执行一致性校验,与 mainGoal 对比
|
|
548
|
+
const mainGoal = this.checkpoint.mainGoal;
|
|
549
|
+
const consistencyResult = this.consistencyChecker.checkConsistency(mainGoal, newGoal);
|
|
550
|
+
this.checkpoint.currentGoal = newGoal;
|
|
551
|
+
this.checkpoint.goalChangeSeq++;
|
|
552
|
+
this.touchCheckpoint();
|
|
553
|
+
// 目标变更是会话的语义骨架,立即清脏(纯内存,无落盘)
|
|
554
|
+
this.settleCheckpoint();
|
|
555
|
+
// 检测到漂移则发射 goalDriftDetected 事件;drift 级自动低风险暂停强制用户确认(不计入连续计数)
|
|
556
|
+
if (consistencyResult.level !== 'same') {
|
|
557
|
+
this.emitEvent('goalDriftDetected', {
|
|
558
|
+
sessionId: this.checkpoint.sessionId,
|
|
559
|
+
mainGoal,
|
|
560
|
+
newGoal,
|
|
561
|
+
similarity: consistencyResult.similarity,
|
|
562
|
+
level: consistencyResult.level,
|
|
563
|
+
constraints: consistencyResult.constraints,
|
|
564
|
+
goalChangeSeq: this.checkpoint.goalChangeSeq,
|
|
565
|
+
});
|
|
566
|
+
if (consistencyResult.level === 'drift') {
|
|
567
|
+
const pauseReason = `目标漂移:新目标与原始目标不一致(相似度 ${consistencyResult.similarity.toFixed(2)})`;
|
|
568
|
+
this.pause(pauseReason, 'system', true);
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
return consistencyResult;
|
|
572
|
+
}
|
|
573
|
+
/** 全量替换计划任务项(与 appendPlanItem 仅追加正交;保留已有任务项 id/status)。唯一生产点:task_table_write mode='replace' */
|
|
574
|
+
updatePlan(plan) {
|
|
575
|
+
if (!this.checkpoint)
|
|
576
|
+
return;
|
|
577
|
+
this.checkpoint.plan = plan;
|
|
578
|
+
this.touchCheckpoint();
|
|
579
|
+
}
|
|
580
|
+
// ── 执行计划管理:SSOT 写点 ──────────────────────────────
|
|
581
|
+
/** 追加计划任务项:在 plan 末尾追加新任务项,不重排已有 order。rolePack 为会议表层装配角色(可选) */
|
|
582
|
+
appendPlanItem(description, rolePack) {
|
|
583
|
+
if (!this.checkpoint)
|
|
584
|
+
return 0;
|
|
585
|
+
const newOrder = this.checkpoint.plan.length;
|
|
586
|
+
const planItem = {
|
|
587
|
+
id: crypto.randomUUID(),
|
|
588
|
+
order: newOrder,
|
|
589
|
+
description,
|
|
590
|
+
status: 'pending',
|
|
591
|
+
...(rolePack ? { rolePack } : {}),
|
|
592
|
+
};
|
|
593
|
+
this.checkpoint.plan.push(planItem);
|
|
594
|
+
this.touchCheckpoint();
|
|
595
|
+
return this.checkpoint.plan.length;
|
|
596
|
+
}
|
|
597
|
+
/**
|
|
598
|
+
* 写入执行计划(计划写入口唯一分发点)。模式语义:
|
|
599
|
+
* 'overwrite':先清空现有 plan 再逐条追加(真重写——LLM task_table_write / 会议预置都依赖此语义,
|
|
600
|
+
* 若与 'append' 同分支只追加不清空,则 overwrite 名存实亡);
|
|
601
|
+
* 'append' :在现有 plan 后逐条追加;
|
|
602
|
+
* 'replace' :全量替换现有 plan(保留已有 id/status/rolePack,仅覆盖 description)。
|
|
603
|
+
* 其它 mode 兜底 no-op。
|
|
604
|
+
* 分发逻辑归本类承载,由 sessionCheckpointLifecycle.test.ts 覆盖。
|
|
605
|
+
*/
|
|
606
|
+
writePlan(mode, items) {
|
|
607
|
+
// 写点自愈:checkpoint 未就绪时先创建(任务表写点 = 任务上下文就绪点)。
|
|
608
|
+
// 若静默 return [] 会让 LLM 收到 ok:true + 0 个任务项 → 伪成功 → 反复重写(实测 6 次)。
|
|
609
|
+
// 不改变已有 checkpoint 时的行为(仅补前置就绪);约会骨架(SeedPrepare 内 writePlan('overwrite'))与普通 task_table_write 同路径生效。
|
|
610
|
+
const cp = this.checkpoint ?? this.createCheckpoint();
|
|
611
|
+
const existingPlan = cp.plan;
|
|
612
|
+
if (mode === 'overwrite' || mode === 'append') {
|
|
613
|
+
// overwrite 真清空:order 由 appendPlanItem 按清空后 plan.length 从 0 重建,自洽无需额外维护
|
|
614
|
+
if (mode === 'overwrite') {
|
|
615
|
+
cp.plan = [];
|
|
616
|
+
}
|
|
617
|
+
for (const item of items) {
|
|
618
|
+
this.appendPlanItem(item.description, item.rolePack);
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
else if (mode === 'replace') {
|
|
622
|
+
const updatedPlan = items.map((s, i) => {
|
|
623
|
+
const existing = existingPlan[i];
|
|
624
|
+
return existing
|
|
625
|
+
? { ...existing, description: s.description }
|
|
626
|
+
: {
|
|
627
|
+
id: crypto.randomUUID(),
|
|
628
|
+
order: i,
|
|
629
|
+
description: s.description,
|
|
630
|
+
status: 'pending',
|
|
631
|
+
...(s.rolePack ? { rolePack: s.rolePack } : {}),
|
|
632
|
+
};
|
|
633
|
+
});
|
|
634
|
+
this.updatePlan(updatedPlan);
|
|
635
|
+
}
|
|
636
|
+
// 确保新写入/追加/更新后的 plan 有 active 任务项(overwrite 清空后全 pending → 激活第一个)
|
|
637
|
+
this.ensureActivePlanItem();
|
|
638
|
+
return cp.plan;
|
|
639
|
+
}
|
|
640
|
+
/**
|
|
641
|
+
* 更新计划任务项状态(plan 任务项状态的唯一写点)。必须经此写点置 checkpointDirty,
|
|
642
|
+
* 否则状态变更可能丢失标脏,计划变更与标脏在此原子完成。
|
|
643
|
+
*
|
|
644
|
+
* 写完后自动 ensureActivePlanItem:如果变更导致 active 空缺(如把 active 标记为 done/blocked),
|
|
645
|
+
* 则推进下一个 pending → active。这保证任何时刻 plan 中恰好有一个 active 任务项。
|
|
646
|
+
*/
|
|
647
|
+
updatePlanItemStatus(planItemId, status) {
|
|
648
|
+
const planItem = this.checkpoint?.plan.find((s) => s.id === planItemId);
|
|
649
|
+
if (!planItem)
|
|
650
|
+
return false;
|
|
651
|
+
planItem.status = status;
|
|
652
|
+
// 确保 active 任务项存在且正确推进(把 active 标记为 done/blocked 后自动激活下一个 pending)
|
|
653
|
+
this.ensureActivePlanItem();
|
|
654
|
+
this.touchCheckpoint();
|
|
655
|
+
return true;
|
|
656
|
+
}
|
|
657
|
+
/**
|
|
658
|
+
* 确保 plan 中存在且仅存在一个 active 任务项(SSOT 自维护)。
|
|
659
|
+
*
|
|
660
|
+
* 三种场景会触发补偿:
|
|
661
|
+
* 1. 全 pending,无 active → 激活第一个 pending(plan 刚写入时)
|
|
662
|
+
* 2. 有 active,且刚被标记为 done/blocked → 激活下一个 pending(推进语义)
|
|
663
|
+
* 3. 无 active 且全 done/blocked → 空操作(plan 已完成)
|
|
664
|
+
*
|
|
665
|
+
* 不在 updatePlanItemStatus 外部重复调用——它在每次写点后自动执行。
|
|
666
|
+
*/
|
|
667
|
+
ensureActivePlanItem() {
|
|
668
|
+
if (!this.checkpoint)
|
|
669
|
+
return;
|
|
670
|
+
const plan = this.checkpoint.plan;
|
|
671
|
+
if (plan.length === 0)
|
|
672
|
+
return;
|
|
673
|
+
// 已有 active 任务项 → 什么都不做。
|
|
674
|
+
// 注:如果多 active 同时存在 → 脏数据(历史 checkpoint 迁移/外部旁路写可能产生);
|
|
675
|
+
// 此处不修、只保单调一,上层单一写点(writePlan + updatePlanItemStatus)契约保证不会产生多 active。
|
|
676
|
+
const hasActive = plan.some((s) => s.status === 'active');
|
|
677
|
+
if (hasActive)
|
|
678
|
+
return;
|
|
679
|
+
// 找到第一个 pending → 激活
|
|
680
|
+
const firstPending = plan.find((s) => s.status === 'pending');
|
|
681
|
+
if (firstPending) {
|
|
682
|
+
firstPending.status = 'active';
|
|
683
|
+
// 不 touchCheckpoint——调用方(updatePlanItemStatus / writePlan)会统一标脏
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
/**
|
|
687
|
+
* 是否存在「在途计划」= 有未完成任务项(pending 或 active)。
|
|
688
|
+
*
|
|
689
|
+
* SSOT:全库该命题唯一实现。消费方三处——会议骨架预置守卫(prepare,判断续会不重开)、
|
|
690
|
+
* 任务表 nudge 跳过(loop,已有在途表则不再诱导建表)、canContinueWithoutInput(agent,
|
|
691
|
+
* 可续跑信号)。禁任一消费方自行内联谓词。
|
|
692
|
+
*
|
|
693
|
+
* 口径取 pending||active(非仅 active):与 ensureActivePlanItem 的补偿语义对齐——
|
|
694
|
+
* plan 非空时恒有一个 active(见 :826 场景 1/2),全 done/blocked 时无 active(场景 3),
|
|
695
|
+
* 两种判法在「有未完成任务项」上等价,但本口径直接表达「未完成」而非「借 active 存在性」。
|
|
696
|
+
*/
|
|
697
|
+
hasInflightPlan() {
|
|
698
|
+
return (this.checkpoint?.plan ?? []).some((s) => s.status === 'pending' || s.status === 'active');
|
|
699
|
+
}
|
|
700
|
+
/** 完成一个任务项(显式完成原语):单函数内顺序写任务项状态 + planItemLog + heartbeat 保证原子性。
|
|
701
|
+
* 消费方 = turn 收尾兜底 concludeActivePlanItemIfPlanFullyReached(LLM 未显式 task_table_update
|
|
702
|
+
* 的最后一个任务项补标);迭代边界 onPlanItemBoundary 只走 logPlanItemBoundary 写日志,
|
|
703
|
+
* 不经本方法推进。 */
|
|
704
|
+
completePlanItem(options) {
|
|
705
|
+
if (!this.checkpoint)
|
|
706
|
+
return;
|
|
707
|
+
// 标记任务项状态(经 updatePlanItemStatus 单一写点,避免旁路契约)
|
|
708
|
+
const { planItemId, summary } = options;
|
|
709
|
+
if (planItemId) {
|
|
710
|
+
this.updatePlanItemStatus(planItemId, 'done');
|
|
711
|
+
}
|
|
712
|
+
this.appendPlanItemLog({ planItemId, summary });
|
|
713
|
+
// 心跳 + 清脏:step 边界即检查点语义边界(纯内存态;检查点不落盘,崩溃走中断轮补全)
|
|
714
|
+
this.touchCheckpoint();
|
|
715
|
+
this.settleCheckpoint();
|
|
716
|
+
}
|
|
717
|
+
/** 写 step 边界日志(时间轴投影,不改 plan 状态)。迭代边界
|
|
718
|
+
* onPlanItemBoundary 只做本写——plan 状态推进唯一写者 = LLM 的 task_table_update。 */
|
|
719
|
+
logPlanItemBoundary(options) {
|
|
720
|
+
if (!this.checkpoint)
|
|
721
|
+
return;
|
|
722
|
+
this.appendPlanItemLog(options);
|
|
723
|
+
this.touchCheckpoint();
|
|
724
|
+
this.settleCheckpoint();
|
|
725
|
+
}
|
|
726
|
+
/**
|
|
727
|
+
* 兜底收尾(「LLM 未显式 update 即收尾」):turn 正常完成且计划已「全部到达」——
|
|
728
|
+
* 存在 active 任务项且无 pending 任务项(LLM 已显式完成所有更早任务项、当前项为最后到达的一项)——
|
|
729
|
+
* 时闭合该 active 任务项(completePlanItem:标 done + planItemLog)。LLM 忘标最后一个任务项时由本兜底补上,
|
|
730
|
+
* 使计划达到全 done(任务表 turn 内收敛,turn 结束兜底清理);真实多轮任务(有 pending)不受影响。
|
|
731
|
+
* 触发点 = seed/orchestrator.act 正常收尾分支(非暂停/中断/失败)。
|
|
732
|
+
*/
|
|
733
|
+
concludeActivePlanItemIfPlanFullyReached(summary) {
|
|
734
|
+
if (!this.checkpoint)
|
|
735
|
+
return;
|
|
736
|
+
const plan = this.checkpoint.plan;
|
|
737
|
+
if (plan.length === 0)
|
|
738
|
+
return;
|
|
739
|
+
// 仍有 pending = 后续任务项未到达 → 真实多轮可续跑语义,不闭合
|
|
740
|
+
if (plan.some((s) => s.status === 'pending'))
|
|
741
|
+
return;
|
|
742
|
+
const active = plan.find((s) => s.status === 'active');
|
|
743
|
+
if (!active)
|
|
744
|
+
return;
|
|
745
|
+
this.completePlanItem({ planItemId: active.id, summary });
|
|
746
|
+
}
|
|
747
|
+
/**
|
|
748
|
+
* 追加任务项日志(completePlanItem / logPlanItemBoundary 共用)。按 planItemId
|
|
749
|
+
* 分组截断,每任务项最多 3 条——若用全局 FIFO 12 条,5+ 任务项的任务中旧任务项的运行记录会被
|
|
750
|
+
* 整段截没,用户翻旧已完成任务项的摘要看到「空」。
|
|
751
|
+
*/
|
|
752
|
+
appendPlanItemLog(options) {
|
|
753
|
+
if (!this.checkpoint)
|
|
754
|
+
return;
|
|
755
|
+
const { planItemId, summary } = options;
|
|
756
|
+
const outcome = {
|
|
757
|
+
planItemId,
|
|
758
|
+
summary,
|
|
759
|
+
completedAt: Date.now(),
|
|
760
|
+
};
|
|
761
|
+
if (!this.checkpoint.planItemLog) {
|
|
762
|
+
this.checkpoint.planItemLog = [];
|
|
763
|
+
}
|
|
764
|
+
this.checkpoint.planItemLog.push(outcome);
|
|
765
|
+
// 每任务项截断上限:同 planItemId(含 undefined 兜底组)超过 3 条时移除最早进入的超出记录
|
|
766
|
+
const PLAN_ITEM_LOG_PER_ITEM_LIMIT = 3;
|
|
767
|
+
const groupCount = this.checkpoint.planItemLog.filter((s) => s.planItemId === planItemId).length;
|
|
768
|
+
if (groupCount > PLAN_ITEM_LOG_PER_ITEM_LIMIT) {
|
|
769
|
+
let excess = groupCount - PLAN_ITEM_LOG_PER_ITEM_LIMIT;
|
|
770
|
+
this.checkpoint.planItemLog = this.checkpoint.planItemLog.filter((s) => {
|
|
771
|
+
if (excess > 0 && s.planItemId === planItemId) {
|
|
772
|
+
excess -= 1;
|
|
773
|
+
return false;
|
|
774
|
+
}
|
|
775
|
+
return true;
|
|
776
|
+
});
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
/** 卸载运行态挂载物:任务流结束/转 idle 时清空 plan/planItemLog(SSOT 资源层 vs 状态层模型),回到"空闲=无挂载物"常态;与 updatePlan(运行态维护)正交 */
|
|
780
|
+
clearPlan() {
|
|
781
|
+
if (!this.checkpoint)
|
|
782
|
+
return;
|
|
783
|
+
this.checkpoint.plan = [];
|
|
784
|
+
this.checkpoint.planItemLog = undefined;
|
|
785
|
+
this.touchCheckpoint();
|
|
786
|
+
this.settleCheckpoint();
|
|
787
|
+
}
|
|
788
|
+
/** 设置暂停元数据;传 undefined 清除。写后必须清脏(清除也清脏,纯内存态无磁盘残留) */
|
|
789
|
+
setPauseMeta(meta) {
|
|
790
|
+
// 意图:pauseMeta 通常随 pause()(建检查点后)写入,但检查点不一定已存在(首轮/未知状态),
|
|
791
|
+
// 故保留防御性守卫——无检查点时静默 no-op,不抛弃 pause/resume 链路的其余语义。
|
|
792
|
+
if (!this.checkpoint)
|
|
793
|
+
return;
|
|
794
|
+
this.checkpoint.pauseMeta = meta;
|
|
795
|
+
this.touchCheckpoint();
|
|
796
|
+
this.settleCheckpoint();
|
|
797
|
+
}
|
|
798
|
+
/**
|
|
799
|
+
* 计划是否全部阻塞(预判短路收窄专用判定):仅全 blocked 视为真停滞。
|
|
800
|
+
* 全 done / 计划空不视为"需要拦截"——用户主动点「继续」= 要 AI 产出,
|
|
801
|
+
* 全 done 可能只是本步收尾(还有总结未说出),空计划是普通问答暂停续跑,都应放行调 LLM。
|
|
802
|
+
* 口径警告:不得把空计划/全 done 判为停滞,否则会误拦正常续跑。
|
|
803
|
+
*/
|
|
804
|
+
isPlanAllBlocked() {
|
|
805
|
+
if (!this.checkpoint)
|
|
806
|
+
return false;
|
|
807
|
+
const { plan } = this.checkpoint;
|
|
808
|
+
return plan.length > 0 && plan.every((s) => s.status === 'blocked');
|
|
809
|
+
}
|
|
810
|
+
/** 获取下一个 pending 任务项(只读不推进,供上下文注入) */
|
|
811
|
+
getNextPendingPlanItem() {
|
|
812
|
+
if (!this.checkpoint)
|
|
813
|
+
return null;
|
|
814
|
+
return this.checkpoint.plan.find((s) => s.status === 'pending') ?? null;
|
|
815
|
+
}
|
|
816
|
+
/** 获取当前 active 任务项 */
|
|
817
|
+
getActivePlanItem() {
|
|
818
|
+
if (!this.checkpoint)
|
|
819
|
+
return null;
|
|
820
|
+
return this.checkpoint.plan.find((s) => s.status === 'active') ?? null;
|
|
821
|
+
}
|
|
822
|
+
// ── 工具执行日志(outbox 模式):补偿管线不启用,以下仅日志 ──
|
|
823
|
+
/**
|
|
824
|
+
* 记录工具执行:追加到检查点日志(append-only,内存态)。
|
|
825
|
+
* 标脏策略:仅标脏不即时清脏(completePlanItem step 边界统一清脏、createCheckpoint 暂停/异常强制清脏)。
|
|
826
|
+
* 检查点纯内存态不落盘:completedToolCalls 仅内存态,回合中途崩溃即整体丢弃、走中断轮补全恢复;
|
|
827
|
+
* 「工具重跑排重」仅在单进程存活期内有效,无跨重启持久化。
|
|
828
|
+
*/
|
|
829
|
+
logToolExecution(record) {
|
|
830
|
+
if (!this.checkpoint)
|
|
831
|
+
return;
|
|
832
|
+
if (!this.checkpoint.completedToolCalls) {
|
|
833
|
+
this.checkpoint.completedToolCalls = [];
|
|
834
|
+
}
|
|
835
|
+
this.checkpoint.completedToolCalls.push(record);
|
|
836
|
+
// FIFO 封顶,优先丢幂等最早记录、非幂等永不丢弃。注意 idempotent 是 IdempotencyLevel 字符串
|
|
837
|
+
// ('non-idempotent' 也 truthy),不能用 truthy 判断,必须显式排除 'non-idempotent'
|
|
838
|
+
if (this.checkpoint.completedToolCalls.length > AGENT_CONSTANTS.COMPLETED_TOOL_CALLS_MAX) {
|
|
839
|
+
const discardable = this.checkpoint.completedToolCalls.findIndex((r) => r.idempotent !== 'non-idempotent');
|
|
840
|
+
if (discardable >= 0) {
|
|
841
|
+
this.checkpoint.completedToolCalls.splice(discardable, 1);
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
this.touchCheckpoint();
|
|
845
|
+
// 依赖 completePlanItem / createCheckpoint 在 step 边界统一清脏
|
|
846
|
+
}
|
|
847
|
+
/** 记录非幂等工具执行(补偿降级后仅日志):不再逐副作用执行补偿,仅记录事实供宿主/人工排查 */
|
|
848
|
+
compensateTool(record) {
|
|
849
|
+
const msg = `${record.name}(${record.argsSignature.slice(0, 50)}):非幂等工具,需人工确认是否需要补偿`;
|
|
850
|
+
logger.warn({ tool: record.name, argsSignature: record.argsSignature.slice(0, 80) }, msg);
|
|
851
|
+
return msg;
|
|
852
|
+
}
|
|
853
|
+
/** 记录所有非幂等工具执行(补偿降级后仅日志):直接记录所有非幂等工具,每条生成一条日志 */
|
|
854
|
+
compensateAllNonIdempotent() {
|
|
855
|
+
if (!this.checkpoint?.completedToolCalls)
|
|
856
|
+
return [];
|
|
857
|
+
return this.checkpoint.completedToolCalls
|
|
858
|
+
.filter((r) => r.idempotent === 'non-idempotent')
|
|
859
|
+
.map((r) => this.compensateTool(r));
|
|
860
|
+
}
|
|
861
|
+
/** 发送心跳,防僵尸会话 */
|
|
862
|
+
heartbeat() {
|
|
863
|
+
if (this.checkpoint) {
|
|
864
|
+
this.touchCheckpoint();
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
/**
|
|
868
|
+
* 标记会话暂停超时(超时事实唯一写点)。广播式:超时信息经事件载荷传递,多监听器并行消费互不干扰。
|
|
869
|
+
* 由 checkPauseTimeout 调用(唯一调用点,启动路径无跨重启恢复)。
|
|
870
|
+
* 会话标识不符 YYYY-MM-DD-<会话名> 时 date/session 缺省但仍发射事件。
|
|
871
|
+
*/
|
|
872
|
+
markSessionTimedOut(sessionId, pauseDuration) {
|
|
873
|
+
const payload = { sessionId, pauseDuration };
|
|
874
|
+
// 严格校验(SSOT:isValidSessionId 单一真源);合法才拆解,非法走 warn 兜底
|
|
875
|
+
if (isValidSessionId(sessionId)) {
|
|
876
|
+
const { date, session } = splitSessionId(sessionId);
|
|
877
|
+
payload.date = date;
|
|
878
|
+
payload.session = session;
|
|
879
|
+
}
|
|
880
|
+
else {
|
|
881
|
+
logger.warn({ sessionId }, '暂停超时会话标识不符合 YYYY-MM-DD-<会话名> 约定,跳过自动归档');
|
|
882
|
+
}
|
|
883
|
+
this.emitEvent('sessionPauseTimedOut', payload);
|
|
884
|
+
}
|
|
885
|
+
// ── 运行时暂停超时检测 ────────────────────────────────
|
|
886
|
+
/** 启动暂停超时检测定时器(仅 paused 态生效):超时自动清理检查点、重置状态机、发射事件。先 stop 防重复启动 */
|
|
887
|
+
startPauseTimeoutTimer() {
|
|
888
|
+
this.stopPauseTimeoutTimer(); // 确保不重复启动
|
|
889
|
+
this._pauseTimeoutTimer = setInterval(() => {
|
|
890
|
+
this.checkPauseTimeout();
|
|
891
|
+
}, SessionManager.PAUSE_TIMEOUT_CHECK_INTERVAL);
|
|
892
|
+
// 允许定时器不阻止进程退出(Node.js unref)
|
|
893
|
+
if (typeof this._pauseTimeoutTimer === 'object' && 'unref' in this._pauseTimeoutTimer) {
|
|
894
|
+
this._pauseTimeoutTimer.unref();
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
/** 停止暂停超时检测定时器 */
|
|
898
|
+
stopPauseTimeoutTimer() {
|
|
899
|
+
if (this._pauseTimeoutTimer !== null) {
|
|
900
|
+
clearInterval(this._pauseTimeoutTimer);
|
|
901
|
+
this._pauseTimeoutTimer = null;
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
/** 检查暂停超时并在超时时自动清理:清理检查点、重置状态机、发射事件并停止定时器 */
|
|
905
|
+
checkPauseTimeout() {
|
|
906
|
+
const checkpoint = this.checkpoint;
|
|
907
|
+
if (!checkpoint || checkpoint.status !== 'paused')
|
|
908
|
+
return;
|
|
909
|
+
if (!this.isPauseTimedOut(checkpoint))
|
|
910
|
+
return;
|
|
911
|
+
// 暂停超时,自动清理
|
|
912
|
+
const sessionId = checkpoint.sessionId;
|
|
913
|
+
const pauseDuration = Date.now() - (checkpoint.pausedAt ?? checkpoint.lastHeartbeat);
|
|
914
|
+
logger.warn({ sessionId, pauseDuration }, '运行时检测到暂停超时,自动清理检查点');
|
|
915
|
+
// 清除检查点 + 状态机回到 running;残留脏标记无对应内存态一并清除
|
|
916
|
+
this.checkpoint = null;
|
|
917
|
+
this.checkpointDirty = false;
|
|
918
|
+
this.stateMachine.resetToRunning();
|
|
919
|
+
// 暂停超时意味着会话断裂,重置连续暂停计数(递增→衰减→重置闭合)
|
|
920
|
+
this.resetConsecutivePauseCount();
|
|
921
|
+
// 记录超时会话 + 发射事件(唯一入口)
|
|
922
|
+
this.markSessionTimedOut(sessionId, pauseDuration);
|
|
923
|
+
// 超时后不再需要继续检测
|
|
924
|
+
this.stopPauseTimeoutTimer();
|
|
925
|
+
}
|
|
926
|
+
/** 关闭时清脏(纯内存,无落盘):Agent 关闭、destroy 前调用。检查点不落盘,
|
|
927
|
+
* 本方法为保留语义边界的 no-op 清脏(无持久化动作) */
|
|
928
|
+
flushOnShutdown() {
|
|
929
|
+
this.settleCheckpoint(true);
|
|
930
|
+
}
|
|
931
|
+
/** 立即清脏(纯内存,无落盘):非只读工具完成后调用。⚠️ 检查点不落盘,completedToolCalls 仅内存态——
|
|
932
|
+
* 「工具重跑排重」仅在单进程存活期内生效,进程崩溃/重启后失效(走中断轮补全)。无落盘设计,
|
|
933
|
+
* 本方法为保留语义边界的 no-op 清脏 */
|
|
934
|
+
flushNow() {
|
|
935
|
+
this.settleCheckpoint(true);
|
|
936
|
+
}
|
|
937
|
+
/** 销毁 SessionManager,清理所有定时器(防阻止进程退出或悬空回调) */
|
|
938
|
+
destroy() {
|
|
939
|
+
this.stopPauseTimeoutTimer();
|
|
940
|
+
}
|
|
941
|
+
/**
|
|
942
|
+
* 检查暂停是否超时:仅对 paused 态,距离暂停起点(pausedAt,缺失回退 lastHeartbeat)超过 PAUSE_TIMEOUT_MS 视为超时。
|
|
943
|
+
* pausedAt 与 lastHeartbeat 解耦,避免暂停后 touchCheckpoint 刷新心跳让超时判定被无限推迟。超时会话自动清理不再恢复。
|
|
944
|
+
*/
|
|
945
|
+
isPauseTimedOut(checkpoint) {
|
|
946
|
+
if (checkpoint.status !== 'paused')
|
|
947
|
+
return false;
|
|
948
|
+
const pauseStart = checkpoint.pausedAt ?? checkpoint.lastHeartbeat;
|
|
949
|
+
return Date.now() - pauseStart > AGENT_CONSTANTS.PAUSE_TIMEOUT_MS;
|
|
189
950
|
}
|
|
190
951
|
}
|
|
191
952
|
//# sourceMappingURL=sessionManager.js.map
|