axon-code 2.5.1 → 2.6.1
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/dist/config/axon-md-parser.d.ts.map +1 -1
- package/dist/config/axon-md-parser.js +21 -4
- package/dist/config/axon-md-parser.js.map +1 -1
- package/dist/config/index.d.ts +9 -6
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +1 -0
- package/dist/config/index.js.map +1 -1
- package/dist/context/enhanced.d.ts.map +1 -1
- package/dist/context/enhanced.js +5 -0
- package/dist/context/enhanced.js.map +1 -1
- package/dist/core/loop.d.ts +45 -1
- package/dist/core/loop.d.ts.map +1 -1
- package/dist/core/loop.js +317 -111
- package/dist/core/loop.js.map +1 -1
- package/dist/core/max-tokens.d.ts.map +1 -1
- package/dist/core/max-tokens.js +5 -0
- package/dist/core/max-tokens.js.map +1 -1
- package/dist/core/session.d.ts.map +1 -1
- package/dist/core/session.js +7 -14
- package/dist/core/session.js.map +1 -1
- package/dist/daemon/config.d.ts +6 -6
- package/dist/daemon/executor.d.ts.map +1 -1
- package/dist/daemon/executor.js +24 -30
- package/dist/daemon/executor.js.map +1 -1
- package/dist/mcp/config.d.ts +8 -8
- package/dist/media/index.d.ts +1 -1
- package/dist/media/index.d.ts.map +1 -1
- package/dist/media/index.js +3 -3
- package/dist/media/index.js.map +1 -1
- package/dist/media/office.d.ts +17 -0
- package/dist/media/office.d.ts.map +1 -1
- package/dist/media/office.js +227 -2
- package/dist/media/office.js.map +1 -1
- package/dist/memory/memory-search.d.ts +4 -1
- package/dist/memory/memory-search.d.ts.map +1 -1
- package/dist/memory/memory-search.js +31 -2
- package/dist/memory/memory-search.js.map +1 -1
- package/dist/memory/memory-sync.d.ts +7 -0
- package/dist/memory/memory-sync.d.ts.map +1 -1
- package/dist/memory/memory-sync.js +88 -0
- package/dist/memory/memory-sync.js.map +1 -1
- package/dist/memory/notebook.d.ts.map +1 -1
- package/dist/memory/notebook.js +562 -15
- package/dist/memory/notebook.js.map +1 -1
- package/dist/models/config.d.ts.map +1 -1
- package/dist/models/config.js +7 -4
- package/dist/models/config.js.map +1 -1
- package/dist/models/index.d.ts +1 -0
- package/dist/models/index.d.ts.map +1 -1
- package/dist/models/index.js +1 -0
- package/dist/models/index.js.map +1 -1
- package/dist/models/model-limits.d.ts +13 -0
- package/dist/models/model-limits.d.ts.map +1 -0
- package/dist/models/model-limits.js +79 -0
- package/dist/models/model-limits.js.map +1 -0
- package/dist/prompt/attachments.d.ts +1 -0
- package/dist/prompt/attachments.d.ts.map +1 -1
- package/dist/prompt/attachments.js +17 -2
- package/dist/prompt/attachments.js.map +1 -1
- package/dist/prompt/cache.d.ts +1 -8
- package/dist/prompt/cache.d.ts.map +1 -1
- package/dist/prompt/cache.js +37 -9
- package/dist/prompt/cache.js.map +1 -1
- package/dist/session/index.d.ts +1 -0
- package/dist/session/index.d.ts.map +1 -1
- package/dist/session/index.js.map +1 -1
- package/dist/tools/agent.d.ts +8 -0
- package/dist/tools/agent.d.ts.map +1 -1
- package/dist/tools/agent.js +66 -2
- package/dist/tools/agent.js.map +1 -1
- package/dist/tools/file.d.ts.map +1 -1
- package/dist/tools/file.js +99 -30
- package/dist/tools/file.js.map +1 -1
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +7 -4
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/mcp.d.ts.map +1 -1
- package/dist/tools/mcp.js +5 -0
- package/dist/tools/mcp.js.map +1 -1
- package/dist/tools/memory-search.d.ts +1 -1
- package/dist/tools/memory-search.d.ts.map +1 -1
- package/dist/tools/memory-search.js +1 -1
- package/dist/tools/memory-search.js.map +1 -1
- package/dist/tools/notebook-write.d.ts.map +1 -1
- package/dist/tools/notebook-write.js +3 -1
- package/dist/tools/notebook-write.js.map +1 -1
- package/dist/tools/skill.d.ts +0 -6
- package/dist/tools/skill.d.ts.map +1 -1
- package/dist/tools/skill.js +211 -34
- package/dist/tools/skill.js.map +1 -1
- package/dist/types/messages.d.ts +1 -5
- package/dist/types/messages.d.ts.map +1 -1
- package/dist/web/server/api-manager.d.ts +7 -5
- package/dist/web/server/api-manager.d.ts.map +1 -1
- package/dist/web/server/api-manager.js +140 -146
- package/dist/web/server/api-manager.js.map +1 -1
- package/dist/web/server/conversation.d.ts.map +1 -1
- package/dist/web/server/conversation.js +27 -10
- package/dist/web/server/conversation.js.map +1 -1
- package/dist/web/server/routes/api.d.ts.map +1 -1
- package/dist/web/server/routes/api.js +21 -24
- package/dist/web/server/routes/api.js.map +1 -1
- package/dist/web/server/routes/axon-cloud.d.ts.map +1 -1
- package/dist/web/server/routes/axon-cloud.js +350 -1
- package/dist/web/server/routes/axon-cloud.js.map +1 -1
- package/dist/web/server/routes/config-api.d.ts.map +1 -1
- package/dist/web/server/routes/config-api.js +12 -20
- package/dist/web/server/routes/config-api.js.map +1 -1
- package/dist/web/server/routes/download-proxy.d.ts.map +1 -1
- package/dist/web/server/routes/download-proxy.js +234 -12
- package/dist/web/server/routes/download-proxy.js.map +1 -1
- package/dist/web/server/runtime/api-connection-test.d.ts +18 -0
- package/dist/web/server/runtime/api-connection-test.d.ts.map +1 -0
- package/dist/web/server/runtime/api-connection-test.js +62 -0
- package/dist/web/server/runtime/api-connection-test.js.map +1 -0
- package/dist/web/server/runtime/codex-client.d.ts +5 -0
- package/dist/web/server/runtime/codex-client.d.ts.map +1 -1
- package/dist/web/server/runtime/codex-client.js +229 -29
- package/dist/web/server/runtime/codex-client.js.map +1 -1
- package/dist/web/server/runtime/runtime-model-catalog.d.ts +10 -0
- package/dist/web/server/runtime/runtime-model-catalog.d.ts.map +1 -0
- package/dist/web/server/runtime/runtime-model-catalog.js +85 -0
- package/dist/web/server/runtime/runtime-model-catalog.js.map +1 -0
- package/dist/web/server/runtime/runtime-model-list.d.ts +24 -0
- package/dist/web/server/runtime/runtime-model-list.d.ts.map +1 -0
- package/dist/web/server/runtime/runtime-model-list.js +43 -0
- package/dist/web/server/runtime/runtime-model-list.js.map +1 -0
- package/dist/web/server/runtime/runtime-selection.d.ts +1 -0
- package/dist/web/server/runtime/runtime-selection.d.ts.map +1 -1
- package/dist/web/server/runtime/runtime-selection.js +5 -3
- package/dist/web/server/runtime/runtime-selection.js.map +1 -1
- package/dist/web/server/runtime/types.d.ts +5 -0
- package/dist/web/server/runtime/types.d.ts.map +1 -1
- package/dist/web/server/runtime/utility-client.d.ts.map +1 -1
- package/dist/web/server/runtime/utility-client.js +4 -1
- package/dist/web/server/runtime/utility-client.js.map +1 -1
- package/dist/web/server/services/axon-cloud-service.d.ts +24 -0
- package/dist/web/server/services/axon-cloud-service.d.ts.map +1 -1
- package/dist/web/server/services/axon-cloud-service.js +93 -0
- package/dist/web/server/services/axon-cloud-service.js.map +1 -1
- package/dist/web/server/services/config-service.d.ts +2 -1
- package/dist/web/server/services/config-service.d.ts.map +1 -1
- package/dist/web/server/services/config-service.js +28 -18
- package/dist/web/server/services/config-service.js.map +1 -1
- package/dist/web/server/session-manager.d.ts +9 -0
- package/dist/web/server/session-manager.d.ts.map +1 -1
- package/dist/web/server/session-manager.js +38 -0
- package/dist/web/server/session-manager.js.map +1 -1
- package/dist/web/server/slash-commands.d.ts.map +1 -1
- package/dist/web/server/slash-commands.js +79 -19
- package/dist/web/server/slash-commands.js.map +1 -1
- package/dist/web/server/task-manager.d.ts +4 -15
- package/dist/web/server/task-manager.d.ts.map +1 -1
- package/dist/web/server/task-manager.js +11 -4
- package/dist/web/server/task-manager.js.map +1 -1
- package/dist/web/server/web-auth.d.ts.map +1 -1
- package/dist/web/server/web-auth.js +32 -28
- package/dist/web/server/web-auth.js.map +1 -1
- package/dist/web/server/websocket.d.ts.map +1 -1
- package/dist/web/server/websocket.js +62 -35
- package/dist/web/server/websocket.js.map +1 -1
- package/dist/web/shared/auth-summary.d.ts.map +1 -1
- package/dist/web/shared/auth-summary.js +5 -2
- package/dist/web/shared/auth-summary.js.map +1 -1
- package/dist/web/shared/model-catalog.d.ts +3 -2
- package/dist/web/shared/model-catalog.d.ts.map +1 -1
- package/dist/web/shared/model-catalog.js +29 -37
- package/dist/web/shared/model-catalog.js.map +1 -1
- package/dist/web/shared/runtime-capabilities.d.ts +37 -0
- package/dist/web/shared/runtime-capabilities.d.ts.map +1 -0
- package/dist/web/shared/runtime-capabilities.js +101 -0
- package/dist/web/shared/runtime-capabilities.js.map +1 -0
- package/dist/web/shared/setup-runtime.d.ts +23 -0
- package/dist/web/shared/setup-runtime.d.ts.map +1 -1
- package/dist/web/shared/setup-runtime.js +119 -19
- package/dist/web/shared/setup-runtime.js.map +1 -1
- package/dist/web/shared/thinking-config.d.ts.map +1 -1
- package/dist/web/shared/thinking-config.js +10 -5
- package/dist/web/shared/thinking-config.js.map +1 -1
- package/dist/web/shared/types.d.ts +8 -0
- package/dist/web/shared/types.d.ts.map +1 -1
- package/dist/web/shared/types.js.map +1 -1
- package/electron/main.cjs +8 -1
- package/package.json +3 -1
- package/src/web/client/dist/assets/index-B2M5Nr-5.js +736 -0
- package/src/web/client/dist/assets/index-COEqamS-.css +32 -0
- package/src/web/client/dist/index.html +2 -2
- package/src/web/client/dist/assets/index-B0gwq5PJ.js +0 -727
- package/src/web/client/dist/assets/index-CwhuMLtk.css +0 -32
package/dist/core/loop.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* 主对话循环
|
|
3
3
|
* 处理用户输入、工具调用和响应
|
|
4
4
|
*/
|
|
5
|
-
import { ClaudeClient, formatSystemPrompt } from './client.js';
|
|
5
|
+
import { ClaudeClient, createClientWithModel, formatSystemPrompt } from './client.js';
|
|
6
6
|
import { Session, setCurrentSessionId } from './session.js';
|
|
7
7
|
import { toolRegistry } from '../tools/index.js';
|
|
8
8
|
import { runWithCwd, runGeneratorWithCwd } from './cwd-context.js';
|
|
@@ -40,7 +40,9 @@ import { configManager } from '../config/index.js';
|
|
|
40
40
|
import { accountUsageManager } from '../ratelimit/index.js';
|
|
41
41
|
import { initNotebookManager, getNotebookManager } from '../memory/notebook.js';
|
|
42
42
|
import { initMemorySearchManager, getMemorySearchManager } from '../memory/memory-search.js';
|
|
43
|
+
import { getResolvedModelContextWindow } from '../models/model-limits.js';
|
|
43
44
|
import { startCacheKeepalive } from './cache-keepalive.js';
|
|
45
|
+
import { createConversationClient } from '../web/server/runtime/factory.js';
|
|
44
46
|
import { estimateTokens } from '../utils/token-estimate.js';
|
|
45
47
|
import { loadActiveGoals } from '../goals/index.js';
|
|
46
48
|
import { isSessionMemoryEnabled as checkSessionMemoryEnabled, SESSION_MEMORY_TEMPLATE, isEmptyTemplate, readSessionMemory, waitForWrite as waitForSessionMemoryWrite, } from '../context/session-memory.js';
|
|
@@ -700,6 +702,10 @@ function calculateTotalTokens(messages) {
|
|
|
700
702
|
* @returns 上下文窗口大小(tokens)
|
|
701
703
|
*/
|
|
702
704
|
export function getContextWindowSize(model) {
|
|
705
|
+
const resolvedContextWindow = getResolvedModelContextWindow(model);
|
|
706
|
+
if (resolvedContextWindow !== undefined) {
|
|
707
|
+
return resolvedContextWindow;
|
|
708
|
+
}
|
|
703
709
|
// 检查是否是 1M 模型(带 [1m] 标记)
|
|
704
710
|
if (model.includes('[1m]')) {
|
|
705
711
|
return 1000000;
|
|
@@ -1500,6 +1506,12 @@ export class ConversationLoop {
|
|
|
1500
1506
|
static TOOL_LOOP_CIRCUIT_BREAKER = 20; // 全局熔断阈值
|
|
1501
1507
|
/** 是否通过构造函数传入了认证信息(跳过 ensureAuthenticated) */
|
|
1502
1508
|
hasExternalAuth = false;
|
|
1509
|
+
/** 自动记忆后台任务,避免同一时刻重复提取 */
|
|
1510
|
+
autoMemorizePromise = null;
|
|
1511
|
+
/** 最近一次尝试提取时看到的可记忆对话消息数,避免同一批消息重复调度 */
|
|
1512
|
+
lastAutoMemorizeAttemptedMessageCount = 0;
|
|
1513
|
+
/** 最近一次成功处理完成的可记忆对话消息数,作为增量提取游标 */
|
|
1514
|
+
lastAutoMemorizedMessageCount = 0;
|
|
1503
1515
|
/**
|
|
1504
1516
|
* 获取当前权限模式 - 官方 v2.1.2 响应式实现
|
|
1505
1517
|
*
|
|
@@ -1581,6 +1593,58 @@ export class ConversationLoop {
|
|
|
1581
1593
|
resetToolCallHistory() {
|
|
1582
1594
|
this.toolCallHistory = [];
|
|
1583
1595
|
}
|
|
1596
|
+
createLoopClient(resolvedModel, options) {
|
|
1597
|
+
if (options.conversationClientConfig) {
|
|
1598
|
+
if (options.conversationClientConfig.apiKey || options.conversationClientConfig.authToken) {
|
|
1599
|
+
this.hasExternalAuth = true;
|
|
1600
|
+
}
|
|
1601
|
+
return createConversationClient({
|
|
1602
|
+
...options.conversationClientConfig,
|
|
1603
|
+
model: options.conversationClientConfig.model || resolvedModel,
|
|
1604
|
+
});
|
|
1605
|
+
}
|
|
1606
|
+
const clientConfig = {
|
|
1607
|
+
model: resolvedModel,
|
|
1608
|
+
maxTokens: options.maxTokens,
|
|
1609
|
+
fallbackModel: options.fallbackModel,
|
|
1610
|
+
thinking: options.thinking,
|
|
1611
|
+
debug: options.debug,
|
|
1612
|
+
timeout: 300000,
|
|
1613
|
+
};
|
|
1614
|
+
if (options.apiKey || options.authToken) {
|
|
1615
|
+
this.hasExternalAuth = true;
|
|
1616
|
+
if (options.apiKey) {
|
|
1617
|
+
clientConfig.apiKey = options.apiKey;
|
|
1618
|
+
}
|
|
1619
|
+
if (options.authToken) {
|
|
1620
|
+
clientConfig.authToken = options.authToken;
|
|
1621
|
+
}
|
|
1622
|
+
if (options.baseUrl) {
|
|
1623
|
+
clientConfig.baseUrl = options.baseUrl;
|
|
1624
|
+
}
|
|
1625
|
+
}
|
|
1626
|
+
else {
|
|
1627
|
+
initAuth();
|
|
1628
|
+
const auth = getAuth();
|
|
1629
|
+
if (auth) {
|
|
1630
|
+
if (auth.type === 'api_key' && auth.apiKey) {
|
|
1631
|
+
clientConfig.apiKey = auth.apiKey;
|
|
1632
|
+
}
|
|
1633
|
+
else if (auth.type === 'oauth') {
|
|
1634
|
+
const scopes = auth.scopes || auth.scope || [];
|
|
1635
|
+
const hasInferenceScope = scopes.includes('user:inference');
|
|
1636
|
+
const oauthToken = auth.authToken || auth.accessToken;
|
|
1637
|
+
if (hasInferenceScope && oauthToken) {
|
|
1638
|
+
clientConfig.authToken = oauthToken;
|
|
1639
|
+
}
|
|
1640
|
+
else if (auth.oauthApiKey) {
|
|
1641
|
+
clientConfig.apiKey = auth.oauthApiKey;
|
|
1642
|
+
}
|
|
1643
|
+
}
|
|
1644
|
+
}
|
|
1645
|
+
}
|
|
1646
|
+
return new ClaudeClient(clientConfig);
|
|
1647
|
+
}
|
|
1584
1648
|
/**
|
|
1585
1649
|
* 处理权限请求(询问用户是否允许工具执行)
|
|
1586
1650
|
* @param toolName 工具名称
|
|
@@ -1707,59 +1771,13 @@ export class ConversationLoop {
|
|
|
1707
1771
|
}
|
|
1708
1772
|
constructor(options = {}) {
|
|
1709
1773
|
// 解析模型别名
|
|
1710
|
-
const resolvedModel = modelConfig.resolveAlias(options.model || 'sonnet');
|
|
1774
|
+
const resolvedModel = options.conversationClientConfig?.model || modelConfig.resolveAlias(options.model || 'sonnet');
|
|
1711
1775
|
// 只有在没有明确指定 isSubAgent 的情况下才设置父模型上下文
|
|
1712
1776
|
// Sub-agent 不应该覆盖全局的父模型上下文
|
|
1713
1777
|
if (!options.isSubAgent) {
|
|
1714
1778
|
setParentModelContext(resolvedModel);
|
|
1715
1779
|
}
|
|
1716
|
-
|
|
1717
|
-
const clientConfig = {
|
|
1718
|
-
model: resolvedModel,
|
|
1719
|
-
maxTokens: options.maxTokens,
|
|
1720
|
-
fallbackModel: options.fallbackModel,
|
|
1721
|
-
thinking: options.thinking,
|
|
1722
|
-
debug: options.debug,
|
|
1723
|
-
timeout: 300000, // 5分钟 API 请求超时
|
|
1724
|
-
};
|
|
1725
|
-
// 如果外部传入了认证信息(如 WebUI 子 agent 复用主 agent 的认证),直接使用
|
|
1726
|
-
if (options.apiKey || options.authToken) {
|
|
1727
|
-
this.hasExternalAuth = true; // 标记为外部认证,跳过 ensureAuthenticated
|
|
1728
|
-
if (options.apiKey) {
|
|
1729
|
-
clientConfig.apiKey = options.apiKey;
|
|
1730
|
-
}
|
|
1731
|
-
if (options.authToken) {
|
|
1732
|
-
clientConfig.authToken = options.authToken;
|
|
1733
|
-
}
|
|
1734
|
-
if (options.baseUrl) {
|
|
1735
|
-
clientConfig.baseUrl = options.baseUrl;
|
|
1736
|
-
}
|
|
1737
|
-
}
|
|
1738
|
-
else {
|
|
1739
|
-
// 默认路径:从本地凭证文件/环境变量初始化认证
|
|
1740
|
-
initAuth();
|
|
1741
|
-
const auth = getAuth();
|
|
1742
|
-
// 根据认证类型设置凭据
|
|
1743
|
-
if (auth) {
|
|
1744
|
-
if (auth.type === 'api_key' && auth.apiKey) {
|
|
1745
|
-
clientConfig.apiKey = auth.apiKey;
|
|
1746
|
-
}
|
|
1747
|
-
else if (auth.type === 'oauth') {
|
|
1748
|
-
// 检查是否有 user:inference scope (Claude.ai 订阅用户)
|
|
1749
|
-
const scopes = auth.scopes || auth.scope || [];
|
|
1750
|
-
const hasInferenceScope = scopes.includes('user:inference');
|
|
1751
|
-
// 获取 OAuth token(可能是 authToken 或 accessToken)
|
|
1752
|
-
const oauthToken = auth.authToken || auth.accessToken;
|
|
1753
|
-
if (hasInferenceScope && oauthToken) {
|
|
1754
|
-
clientConfig.authToken = oauthToken;
|
|
1755
|
-
}
|
|
1756
|
-
else if (auth.oauthApiKey) {
|
|
1757
|
-
clientConfig.apiKey = auth.oauthApiKey;
|
|
1758
|
-
}
|
|
1759
|
-
}
|
|
1760
|
-
}
|
|
1761
|
-
}
|
|
1762
|
-
this.client = new ClaudeClient(clientConfig);
|
|
1780
|
+
this.client = this.createLoopClient(resolvedModel, options);
|
|
1763
1781
|
this.session = new Session();
|
|
1764
1782
|
this.options = options;
|
|
1765
1783
|
this.promptBuilder = systemPromptBuilder;
|
|
@@ -2083,6 +2101,61 @@ export class ConversationLoop {
|
|
|
2083
2101
|
return enrichedInput;
|
|
2084
2102
|
}
|
|
2085
2103
|
}
|
|
2104
|
+
/**
|
|
2105
|
+
* 从用户输入中提取可用于长期记忆召回的自然语言查询
|
|
2106
|
+
*/
|
|
2107
|
+
extractRecallQuery(userInput) {
|
|
2108
|
+
if (typeof userInput === 'string') {
|
|
2109
|
+
const text = userInput.trim();
|
|
2110
|
+
return text.length > 0 ? text : null;
|
|
2111
|
+
}
|
|
2112
|
+
if (!Array.isArray(userInput)) {
|
|
2113
|
+
return null;
|
|
2114
|
+
}
|
|
2115
|
+
const text = userInput
|
|
2116
|
+
.filter((block) => block?.type === 'text' && typeof block.text === 'string')
|
|
2117
|
+
.map((block) => block.text.trim())
|
|
2118
|
+
.filter(Boolean)
|
|
2119
|
+
.join('\n')
|
|
2120
|
+
.trim();
|
|
2121
|
+
return text.length > 0 ? text : null;
|
|
2122
|
+
}
|
|
2123
|
+
/**
|
|
2124
|
+
* 刷新 prompt 里依赖长期记忆的动态上下文
|
|
2125
|
+
*/
|
|
2126
|
+
async refreshPromptMemoryContext(userInput) {
|
|
2127
|
+
try {
|
|
2128
|
+
const notebookMgr = getNotebookManager() || initNotebookManager(this.promptContext.workingDir);
|
|
2129
|
+
const freshSummary = notebookMgr.getNotebookSummaryForPrompt();
|
|
2130
|
+
this.promptContext.notebookSummary = freshSummary || undefined;
|
|
2131
|
+
}
|
|
2132
|
+
catch {
|
|
2133
|
+
// 笔记本加载失败不影响主流程
|
|
2134
|
+
}
|
|
2135
|
+
const recallQuery = this.extractRecallQuery(userInput);
|
|
2136
|
+
if (!recallQuery || recallQuery.trim().length < 3) {
|
|
2137
|
+
this.promptContext.memoryRecall = undefined;
|
|
2138
|
+
return;
|
|
2139
|
+
}
|
|
2140
|
+
try {
|
|
2141
|
+
const memSearchMgr = getMemorySearchManager();
|
|
2142
|
+
if (!memSearchMgr) {
|
|
2143
|
+
this.promptContext.memoryRecall = undefined;
|
|
2144
|
+
return;
|
|
2145
|
+
}
|
|
2146
|
+
const memoryRecall = await memSearchMgr.recall(recallQuery, 3, {
|
|
2147
|
+
source: 'notebook',
|
|
2148
|
+
mode: 'keyword',
|
|
2149
|
+
});
|
|
2150
|
+
this.promptContext.memoryRecall = memoryRecall || undefined;
|
|
2151
|
+
}
|
|
2152
|
+
catch (error) {
|
|
2153
|
+
if (this.options.debug || process.env.AXON_DEBUG) {
|
|
2154
|
+
console.warn('[MemoryRecall] Failed to refresh prompt memory context:', error);
|
|
2155
|
+
}
|
|
2156
|
+
this.promptContext.memoryRecall = undefined;
|
|
2157
|
+
}
|
|
2158
|
+
}
|
|
2086
2159
|
/**
|
|
2087
2160
|
* 检查是否为 Git 仓库
|
|
2088
2161
|
*/
|
|
@@ -2240,9 +2313,8 @@ export class ConversationLoop {
|
|
|
2240
2313
|
let finalResponse = '';
|
|
2241
2314
|
// 解析模型别名(在循环外部,避免重复解析)
|
|
2242
2315
|
const resolvedModel = modelConfig.resolveAlias(this.options.model || 'sonnet');
|
|
2243
|
-
//
|
|
2244
|
-
|
|
2245
|
-
this.promptContext.memoryRecall = undefined;
|
|
2316
|
+
// 在构建 prompt 前刷新 notebook / recall,确保上一轮刚写入的长期记忆能立刻生效
|
|
2317
|
+
await this.refreshPromptMemoryContext(userInput);
|
|
2246
2318
|
// 构建系统提示词
|
|
2247
2319
|
let systemPrompt;
|
|
2248
2320
|
let promptBlocks;
|
|
@@ -2265,20 +2337,6 @@ export class ConversationLoop {
|
|
|
2265
2337
|
systemPrompt = this.getDefaultSystemPrompt();
|
|
2266
2338
|
}
|
|
2267
2339
|
}
|
|
2268
|
-
// Agent 笔记本:每轮刷新笔记本内容到 promptContext
|
|
2269
|
-
// 确保 agent 在对话中写入的笔记能在下一轮 system prompt 中体现
|
|
2270
|
-
try {
|
|
2271
|
-
const nbMgr = getNotebookManager();
|
|
2272
|
-
if (nbMgr) {
|
|
2273
|
-
const freshSummary = nbMgr.getNotebookSummaryForPrompt();
|
|
2274
|
-
if (freshSummary) {
|
|
2275
|
-
this.promptContext.notebookSummary = freshSummary;
|
|
2276
|
-
}
|
|
2277
|
-
}
|
|
2278
|
-
}
|
|
2279
|
-
catch {
|
|
2280
|
-
// 笔记本加载失败不影响主流程
|
|
2281
|
-
}
|
|
2282
2340
|
while (turns < maxTurns) {
|
|
2283
2341
|
turns++;
|
|
2284
2342
|
// v2.1.34: 每个 turn 开始时重置工具调用历史
|
|
@@ -2398,12 +2456,15 @@ export class ConversationLoop {
|
|
|
2398
2456
|
// 并行执行所有工具(对齐官方 KM5 函数:Promise.all(toolUseBlocks.map(...)))
|
|
2399
2457
|
if (toolUseBlocks.length > 0) {
|
|
2400
2458
|
// Cache Keepalive:工具执行期间保持 prompt cache 活跃
|
|
2401
|
-
const
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2459
|
+
const anthropicClient = this.client.getAnthropicClient();
|
|
2460
|
+
const stopKeepalive = anthropicClient
|
|
2461
|
+
? startCacheKeepalive({
|
|
2462
|
+
client: anthropicClient,
|
|
2463
|
+
model: this.client.getModel(),
|
|
2464
|
+
formattedSystem: formatSystemPrompt(systemPrompt, this.client.getIsOAuth(), promptBlocks),
|
|
2465
|
+
debug: this.options.debug || this.options.verbose,
|
|
2466
|
+
})
|
|
2467
|
+
: () => { };
|
|
2407
2468
|
const execResults = await Promise.all(toolUseBlocks.map(async (block) => {
|
|
2408
2469
|
const toolBlock = block;
|
|
2409
2470
|
const toolName = toolBlock.name || '';
|
|
@@ -2572,6 +2633,90 @@ Guidelines:
|
|
|
2572
2633
|
console.error('Failed to auto-save session:', err);
|
|
2573
2634
|
}
|
|
2574
2635
|
}
|
|
2636
|
+
this.scheduleAutoMemorize();
|
|
2637
|
+
}
|
|
2638
|
+
/**
|
|
2639
|
+
* 提取单条消息中的自然语言文本,用于自动记忆。
|
|
2640
|
+
* 跳过 tool_use/tool_result 等结构化块,避免把工具噪音当成用户画像。
|
|
2641
|
+
*/
|
|
2642
|
+
extractAutoMemoryText(message) {
|
|
2643
|
+
if (message.isMeta)
|
|
2644
|
+
return null;
|
|
2645
|
+
if (message.role !== 'user' && message.role !== 'assistant')
|
|
2646
|
+
return null;
|
|
2647
|
+
if (typeof message.content === 'string') {
|
|
2648
|
+
const text = message.content.trim();
|
|
2649
|
+
return text ? text : null;
|
|
2650
|
+
}
|
|
2651
|
+
if (Array.isArray(message.content)) {
|
|
2652
|
+
const texts = message.content
|
|
2653
|
+
.filter((block) => block.type === 'text' && typeof block.text === 'string' && block.text.trim())
|
|
2654
|
+
.map((block) => block.text.trim());
|
|
2655
|
+
return texts.length > 0 ? texts.join(' ') : null;
|
|
2656
|
+
}
|
|
2657
|
+
return null;
|
|
2658
|
+
}
|
|
2659
|
+
/**
|
|
2660
|
+
* 统计适合自动记忆的对话消息数。
|
|
2661
|
+
* 只统计真正的人类/助手自然语言内容,不统计工具结果。
|
|
2662
|
+
*/
|
|
2663
|
+
getAutoMemorizeMessageCount() {
|
|
2664
|
+
return this.session.getMessages().reduce((count, message) => {
|
|
2665
|
+
return this.extractAutoMemoryText(message) ? count + 1 : count;
|
|
2666
|
+
}, 0);
|
|
2667
|
+
}
|
|
2668
|
+
/**
|
|
2669
|
+
* 后台调度自动记忆,避免阻塞用户拿到回复。
|
|
2670
|
+
*/
|
|
2671
|
+
scheduleAutoMemorize() {
|
|
2672
|
+
if (this.options.isSubAgent || this.autoMemorizePromise) {
|
|
2673
|
+
return;
|
|
2674
|
+
}
|
|
2675
|
+
const messageCount = this.getAutoMemorizeMessageCount();
|
|
2676
|
+
if (messageCount < 4 || messageCount <= this.lastAutoMemorizeAttemptedMessageCount) {
|
|
2677
|
+
return;
|
|
2678
|
+
}
|
|
2679
|
+
const startMessageCount = this.lastAutoMemorizedMessageCount;
|
|
2680
|
+
const endMessageCount = messageCount;
|
|
2681
|
+
this.autoMemorizePromise = this.maybeAutoMemorize(startMessageCount, endMessageCount)
|
|
2682
|
+
.catch((error) => {
|
|
2683
|
+
console.error(chalk.gray('[AutoMemory] Background scheduling failed:', error instanceof Error ? error.message : String(error)));
|
|
2684
|
+
})
|
|
2685
|
+
.finally(() => {
|
|
2686
|
+
this.autoMemorizePromise = null;
|
|
2687
|
+
});
|
|
2688
|
+
}
|
|
2689
|
+
/**
|
|
2690
|
+
* 仅对新增对话消息执行一次自动记忆。
|
|
2691
|
+
*/
|
|
2692
|
+
async maybeAutoMemorize(startMessageCount, endMessageCount) {
|
|
2693
|
+
if (endMessageCount <= this.lastAutoMemorizeAttemptedMessageCount) {
|
|
2694
|
+
return;
|
|
2695
|
+
}
|
|
2696
|
+
this.lastAutoMemorizeAttemptedMessageCount = endMessageCount;
|
|
2697
|
+
await this.autoMemorize({
|
|
2698
|
+
startMessageCount,
|
|
2699
|
+
endMessageCount,
|
|
2700
|
+
updateSuccessCursor: true,
|
|
2701
|
+
});
|
|
2702
|
+
}
|
|
2703
|
+
/**
|
|
2704
|
+
* 为自动记忆创建轻量客户端,尽量使用 Haiku 降本。
|
|
2705
|
+
*/
|
|
2706
|
+
createAutoMemoryClient() {
|
|
2707
|
+
const haikuModel = modelConfig.resolveAlias('haiku');
|
|
2708
|
+
if (this.options.apiKey || this.options.authToken || this.options.baseUrl) {
|
|
2709
|
+
return new ClaudeClient({
|
|
2710
|
+
model: haikuModel,
|
|
2711
|
+
apiKey: this.options.apiKey,
|
|
2712
|
+
authToken: this.options.authToken,
|
|
2713
|
+
baseUrl: this.options.baseUrl,
|
|
2714
|
+
timeout: 120000,
|
|
2715
|
+
maxTokens: 4096,
|
|
2716
|
+
debug: this.options.debug,
|
|
2717
|
+
});
|
|
2718
|
+
}
|
|
2719
|
+
return createClientWithModel(haikuModel);
|
|
2575
2720
|
}
|
|
2576
2721
|
/**
|
|
2577
2722
|
* 流式处理用户消息
|
|
@@ -2615,6 +2760,8 @@ Guidelines:
|
|
|
2615
2760
|
// 使用响应式状态获取最新的权限模式
|
|
2616
2761
|
const currentMode = this.getCurrentPermissionMode();
|
|
2617
2762
|
this.promptContext.permissionMode = currentMode;
|
|
2763
|
+
// 每个 turn 开始前刷新 notebook / recall,确保工具刚写入的长期记忆能进入下一轮推理
|
|
2764
|
+
await this.refreshPromptMemoryContext(userInput);
|
|
2618
2765
|
// 每个 turn 重新构建系统提示词 - 支持运行时权限模式切换 (官方 v2.1.2 Shift+Tab)
|
|
2619
2766
|
let systemPrompt;
|
|
2620
2767
|
let promptBlocks;
|
|
@@ -2735,6 +2882,13 @@ Guidelines:
|
|
|
2735
2882
|
tool.input += event.input || '';
|
|
2736
2883
|
}
|
|
2737
2884
|
}
|
|
2885
|
+
else if (event.type === 'tool_use_complete') {
|
|
2886
|
+
const completedToolId = event.id || currentToolId;
|
|
2887
|
+
const tool = toolCalls.get(completedToolId);
|
|
2888
|
+
if (tool && !tool.isServerTool) {
|
|
2889
|
+
tool.input = JSON.stringify(event.input || {});
|
|
2890
|
+
}
|
|
2891
|
+
}
|
|
2738
2892
|
else if (event.type === 'web_search_result') {
|
|
2739
2893
|
// web_search_tool_result 从 finalMessage 中提取,收集搜索结果
|
|
2740
2894
|
const resultBlock = event.data;
|
|
@@ -2918,12 +3072,15 @@ Guidelines:
|
|
|
2918
3072
|
}
|
|
2919
3073
|
// 第三步:并行执行所有工具(核心修复)
|
|
2920
3074
|
// Cache Keepalive:工具执行期间保持 prompt cache 活跃
|
|
2921
|
-
const
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
3075
|
+
const anthropicClient = this.client.getAnthropicClient();
|
|
3076
|
+
const stopKeepalive = anthropicClient
|
|
3077
|
+
? startCacheKeepalive({
|
|
3078
|
+
client: anthropicClient,
|
|
3079
|
+
model: this.client.getModel(),
|
|
3080
|
+
formattedSystem: formatSystemPrompt(systemPrompt, this.client.getIsOAuth(), promptBlocks),
|
|
3081
|
+
debug: this.options.debug || this.options.verbose,
|
|
3082
|
+
})
|
|
3083
|
+
: () => { };
|
|
2927
3084
|
const execPromises = [];
|
|
2928
3085
|
for (const [id, tool] of clientToolCalls) {
|
|
2929
3086
|
const input = parsedInputs.get(id);
|
|
@@ -3118,6 +3275,9 @@ Guidelines:
|
|
|
3118
3275
|
}
|
|
3119
3276
|
setSession(session) {
|
|
3120
3277
|
this.session = session;
|
|
3278
|
+
this.lastAutoMemorizeAttemptedMessageCount = 0;
|
|
3279
|
+
this.lastAutoMemorizedMessageCount = 0;
|
|
3280
|
+
this.autoMemorizePromise = null;
|
|
3121
3281
|
// v2.1.27: 设置全局会话 ID 以供工具使用(如 gh pr create 自动链接)
|
|
3122
3282
|
setCurrentSessionId(session.sessionId);
|
|
3123
3283
|
}
|
|
@@ -3199,20 +3359,31 @@ Guidelines:
|
|
|
3199
3359
|
* - 使用 haiku 模型降低成本,只提取结构化信息
|
|
3200
3360
|
* - 静默失败,不影响退出流程
|
|
3201
3361
|
*/
|
|
3202
|
-
async autoMemorize() {
|
|
3362
|
+
async autoMemorize(options) {
|
|
3203
3363
|
try {
|
|
3204
3364
|
const nbMgr = getNotebookManager();
|
|
3205
3365
|
if (!nbMgr)
|
|
3206
|
-
return;
|
|
3366
|
+
return false;
|
|
3207
3367
|
const messages = this.session.getMessages();
|
|
3208
|
-
|
|
3209
|
-
|
|
3210
|
-
|
|
3368
|
+
const meaningfulMessages = messages.filter((message) => this.extractAutoMemoryText(message));
|
|
3369
|
+
const endMessageCount = Math.min(options?.endMessageCount ?? meaningfulMessages.length, meaningfulMessages.length);
|
|
3370
|
+
const startMessageCount = Math.max(0, Math.min(options?.startMessageCount ?? this.lastAutoMemorizedMessageCount, endMessageCount));
|
|
3371
|
+
const messagesToSummarize = meaningfulMessages.slice(startMessageCount, endMessageCount);
|
|
3372
|
+
const shouldAdvanceSuccessCursor = options?.updateSuccessCursor ?? true;
|
|
3373
|
+
// 对话太短(少于4条有效文本消息 = 2轮对话),没什么可提取的
|
|
3374
|
+
if (endMessageCount < 4 || messagesToSummarize.length === 0)
|
|
3375
|
+
return false;
|
|
3211
3376
|
// 读取当前笔记本内容
|
|
3377
|
+
const currentProfile = nbMgr.read('profile');
|
|
3212
3378
|
const currentExperience = nbMgr.read('experience');
|
|
3213
3379
|
const currentProject = nbMgr.read('project');
|
|
3380
|
+
const today = new Date().toISOString().slice(0, 10);
|
|
3214
3381
|
// 构造提取 prompt
|
|
3215
3382
|
const extractionPrompt = `你是一个记忆提取器。分析以下对话,提取值得跨会话记住的信息。
|
|
3383
|
+
今天日期:${today}
|
|
3384
|
+
|
|
3385
|
+
当前 profile 笔记本内容:
|
|
3386
|
+
${currentProfile || '(空)'}
|
|
3216
3387
|
|
|
3217
3388
|
当前 experience 笔记本内容:
|
|
3218
3389
|
${currentExperience || '(空)'}
|
|
@@ -3222,19 +3393,45 @@ ${currentProject || '(空)'}
|
|
|
3222
3393
|
|
|
3223
3394
|
规则:
|
|
3224
3395
|
1. 只提取真正有长期价值的信息,忽略一次性的技术细节
|
|
3225
|
-
2.
|
|
3226
|
-
3.
|
|
3227
|
-
4.
|
|
3228
|
-
5.
|
|
3229
|
-
6.
|
|
3230
|
-
7.
|
|
3231
|
-
8.
|
|
3232
|
-
9.
|
|
3396
|
+
2. profile 笔记本记录:稳定的用户画像,如身份背景、长期偏好、表达风格、沟通习惯、价值取向
|
|
3397
|
+
3. experience 笔记本记录:如何与用户协作更顺畅的跨项目经验,如工作模式、常见要求、反复纠正点、偏好的做事方式
|
|
3398
|
+
4. project 笔记本记录:项目特有的陷阱、隐藏依赖、重要架构决策、踩过的坑
|
|
3399
|
+
5. profile 只记“相对稳定、以后大概率还成立”的信息;短期状态、临时任务、一次性情绪不要写进 profile
|
|
3400
|
+
6. 如果用户明确纠正了你对他/她的理解,这类纠正优先写入 profile 或 experience
|
|
3401
|
+
7. 如果没有新信息值得记录,返回 NO_UPDATE
|
|
3402
|
+
8. 如果有更新,返回完整的笔记本内容(不是增量,是完整替换)
|
|
3403
|
+
9. profile 不超过 2000 tokens,experience 不超过 4000 tokens,project 不超过 8000 tokens
|
|
3404
|
+
10. 保留原有内容,但要主动合并同义 bullet;如果新证据推翻旧理解,必须改写或删除旧 bullet,不能把互相冲突的理解一起保留
|
|
3405
|
+
11. 不要编造用户信息;必须来自对话证据。没有证据支撑的内容,不要写进事实 section
|
|
3406
|
+
12. 特别注意提取决策链,即“偏好什么 / 讨厌什么 / 会如何表达不满 / 遇到什么最在意”
|
|
3407
|
+
13. 不要输出解释,不要输出 markdown code fence,只输出指定格式
|
|
3408
|
+
14. 如需更新 profile,必须使用以下固定结构和标题,顺序不要变;未知 section 可以留空,但不要删除标题:
|
|
3409
|
+
# User Profile
|
|
3410
|
+
|
|
3411
|
+
## Basic Info
|
|
3412
|
+
## Stable Preferences
|
|
3413
|
+
## Communication Style
|
|
3414
|
+
## Working Style
|
|
3415
|
+
## Decision Signals
|
|
3416
|
+
## Values & Motivations
|
|
3417
|
+
## Do Not Assume / Open Questions
|
|
3418
|
+
15. profile 里的每条 bullet 尽量只表达一个稳定信号,避免把多种偏好揉成一条长句
|
|
3419
|
+
16. profile 的 bullet 优先使用这个轻量格式:- 信号 [updated: YYYY-MM-DD; evidence: 简短证据]
|
|
3420
|
+
17. evidence 只需简短说明证据来源,例如 “user stated directly” / “explicit correction about tone” / “repeatedly requested concise Chinese replies”
|
|
3421
|
+
18. 优先修改已有 bullet,避免同义重复和越写越散
|
|
3422
|
+
19. “Do Not Assume / Open Questions” 只放高相关但仍未确认的信息;一旦对话里已确认,就从 open questions 里删掉,不要继续保留
|
|
3423
|
+
20. experience 更偏“怎么配合这个用户工作”,不要重复 profile 里的稳定画像
|
|
3424
|
+
21. project 只写当前项目相关内容,不要把通用人格特征写进 project
|
|
3233
3425
|
|
|
3234
3426
|
输出格式(严格遵守):
|
|
3235
3427
|
如果无需更新:
|
|
3236
3428
|
NO_UPDATE
|
|
3237
3429
|
|
|
3430
|
+
如果需要更新 profile:
|
|
3431
|
+
===PROFILE===
|
|
3432
|
+
(完整的 profile 笔记本内容)
|
|
3433
|
+
===END_PROFILE===
|
|
3434
|
+
|
|
3238
3435
|
如果需要更新 experience:
|
|
3239
3436
|
===EXPERIENCE===
|
|
3240
3437
|
(完整的 experience 笔记本内容)
|
|
@@ -3245,33 +3442,25 @@ NO_UPDATE
|
|
|
3245
3442
|
(完整的 project 笔记本内容)
|
|
3246
3443
|
===END_PROJECT===
|
|
3247
3444
|
|
|
3248
|
-
|
|
3249
|
-
8. 在笔记本末尾维护一行统计:"<!-- autoMemorize: 更新于 {YYYY-MM-DD}, 累计 N 次 -->",每次更新时 N+1`;
|
|
3445
|
+
可以同时更新三个,也可以只更新其中一个或两个。`;
|
|
3250
3446
|
// 将对话消息精简为文本摘要(只取用户和助手的文本内容,忽略工具调用细节)
|
|
3251
|
-
const conversationSummary =
|
|
3252
|
-
.filter((m) => m.role === 'user' || m.role === 'assistant')
|
|
3447
|
+
const conversationSummary = messagesToSummarize
|
|
3253
3448
|
.map((m) => {
|
|
3449
|
+
const text = this.extractAutoMemoryText(m);
|
|
3450
|
+
if (!text)
|
|
3451
|
+
return null;
|
|
3254
3452
|
const role = m.role === 'user' ? '用户' : '助手';
|
|
3255
|
-
|
|
3256
|
-
|
|
3257
|
-
}
|
|
3258
|
-
if (Array.isArray(m.content)) {
|
|
3259
|
-
const textBlocks = m.content
|
|
3260
|
-
.filter((b) => b.type === 'text' && b.text)
|
|
3261
|
-
.map((b) => b.text.substring(0, m.role === 'user' ? 1000 : 800));
|
|
3262
|
-
if (textBlocks.length > 0) {
|
|
3263
|
-
return `${role}: ${textBlocks.join(' ')}`;
|
|
3264
|
-
}
|
|
3265
|
-
}
|
|
3266
|
-
return null;
|
|
3453
|
+
const limit = m.role === 'user' ? 1000 : 800;
|
|
3454
|
+
return `${role}: ${text.substring(0, limit)}`;
|
|
3267
3455
|
})
|
|
3268
3456
|
.filter(Boolean)
|
|
3269
3457
|
.join('\n');
|
|
3270
3458
|
// 如果对话内容太少,跳过
|
|
3271
3459
|
if (conversationSummary.length < 100)
|
|
3272
|
-
return;
|
|
3460
|
+
return false;
|
|
3273
3461
|
// 使用轻量模型调用 API
|
|
3274
|
-
const
|
|
3462
|
+
const autoMemoryClient = this.createAutoMemoryClient();
|
|
3463
|
+
const response = await autoMemoryClient.createMessage([
|
|
3275
3464
|
{ role: 'user', content: `${extractionPrompt}\n\n===对话内容===\n${conversationSummary.substring(0, 20000)}` },
|
|
3276
3465
|
], [], // 不需要工具
|
|
3277
3466
|
'你是记忆提取器,只输出指定格式,不输出其他内容。');
|
|
@@ -3280,8 +3469,20 @@ NO_UPDATE
|
|
|
3280
3469
|
.filter((b) => b.type === 'text' && b.text)
|
|
3281
3470
|
.map((b) => b.text)
|
|
3282
3471
|
.join('');
|
|
3283
|
-
if (!responseText || responseText.includes('NO_UPDATE'))
|
|
3284
|
-
|
|
3472
|
+
if (!responseText || responseText.includes('NO_UPDATE')) {
|
|
3473
|
+
if (shouldAdvanceSuccessCursor) {
|
|
3474
|
+
this.lastAutoMemorizedMessageCount = endMessageCount;
|
|
3475
|
+
}
|
|
3476
|
+
return true;
|
|
3477
|
+
}
|
|
3478
|
+
// 提取并写入 profile
|
|
3479
|
+
const profileMatch = responseText.match(/===PROFILE===\n([\s\S]*?)\n===END_PROFILE===/);
|
|
3480
|
+
if (profileMatch && profileMatch[1].trim()) {
|
|
3481
|
+
const result = nbMgr.write('profile', profileMatch[1].trim());
|
|
3482
|
+
if (result.success) {
|
|
3483
|
+
console.error(chalk.gray('[AutoMemory] profile notebook updated'));
|
|
3484
|
+
}
|
|
3485
|
+
}
|
|
3285
3486
|
// 提取并写入 experience
|
|
3286
3487
|
const expMatch = responseText.match(/===EXPERIENCE===\n([\s\S]*?)\n===END_EXPERIENCE===/);
|
|
3287
3488
|
if (expMatch && expMatch[1].trim()) {
|
|
@@ -3303,10 +3504,15 @@ NO_UPDATE
|
|
|
3303
3504
|
if (memSearchMgr) {
|
|
3304
3505
|
memSearchMgr.markDirty();
|
|
3305
3506
|
}
|
|
3507
|
+
if (shouldAdvanceSuccessCursor) {
|
|
3508
|
+
this.lastAutoMemorizedMessageCount = endMessageCount;
|
|
3509
|
+
}
|
|
3510
|
+
return true;
|
|
3306
3511
|
}
|
|
3307
3512
|
catch (error) {
|
|
3308
3513
|
// 非静默失败,记录错误信息
|
|
3309
3514
|
console.error(chalk.gray('[AutoMemory] Memory extraction failed:', error instanceof Error ? error.message : String(error)));
|
|
3515
|
+
return false;
|
|
3310
3516
|
}
|
|
3311
3517
|
}
|
|
3312
3518
|
/**
|