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/memory/notebook.js
CHANGED
|
@@ -66,6 +66,547 @@ function ensureDir(dir) {
|
|
|
66
66
|
fs.mkdirSync(dir, { recursive: true });
|
|
67
67
|
}
|
|
68
68
|
}
|
|
69
|
+
const PROFILE_SECTION_DEFINITIONS = [
|
|
70
|
+
{
|
|
71
|
+
title: 'Basic Info',
|
|
72
|
+
aliases: [/^basic info$/i, /^stable facts$/i, /^identity$/i, /^background$/i, /^基本信息$/, /^个人信息$/],
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
title: 'Stable Preferences',
|
|
76
|
+
aliases: [/^stable preferences$/i, /^preferences$/i, /^长期偏好$/i, /^偏好$/i],
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
title: 'Communication Style',
|
|
80
|
+
aliases: [/^communication style$/i, /^communication preferences$/i, /^tone.*style$/i, /^沟通风格$/, /^表达风格$/],
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
title: 'Working Style',
|
|
84
|
+
aliases: [/^working style$/i, /^working preferences$/i, /^collaboration style$/i, /^工作风格$/, /^协作方式$/],
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
title: 'Decision Signals',
|
|
88
|
+
aliases: [/^decision signals$/i, /^friction signals$/i, /^triggers$/i, /^雷区$/, /^信号$/, /^决策信号$/],
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
title: 'Values & Motivations',
|
|
92
|
+
aliases: [/^values\s*&\s*motivations$/i, /^long-term goals.*values$/i, /^values$/i, /^goals$/i, /^价值观与动机$/, /^长期目标与价值$/],
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
title: 'Do Not Assume / Open Questions',
|
|
96
|
+
aliases: [/^do not assume\s*\/\s*open questions$/i, /^open questions$/i, /^unknowns$/i, /^uncertainties$/i, /^待确认事项$/, /^不要假设 \/ 待确认$/],
|
|
97
|
+
},
|
|
98
|
+
];
|
|
99
|
+
const PROFILE_SECTION_ORDER = PROFILE_SECTION_DEFINITIONS.map((section) => section.title);
|
|
100
|
+
const PROFILE_ADDITIONAL_NOTES = 'Additional Notes';
|
|
101
|
+
function buildDefaultProfileTemplate() {
|
|
102
|
+
return [
|
|
103
|
+
'# User Profile',
|
|
104
|
+
'',
|
|
105
|
+
'## Basic Info',
|
|
106
|
+
'- Language preference: (auto-detected)',
|
|
107
|
+
'',
|
|
108
|
+
'## Stable Preferences',
|
|
109
|
+
'',
|
|
110
|
+
'## Communication Style',
|
|
111
|
+
'',
|
|
112
|
+
'## Working Style',
|
|
113
|
+
'',
|
|
114
|
+
'## Decision Signals',
|
|
115
|
+
'',
|
|
116
|
+
'## Values & Motivations',
|
|
117
|
+
'',
|
|
118
|
+
'## Do Not Assume / Open Questions',
|
|
119
|
+
].join('\n');
|
|
120
|
+
}
|
|
121
|
+
function canonicalizeProfileSectionTitle(rawTitle) {
|
|
122
|
+
const normalizedTitle = rawTitle.trim();
|
|
123
|
+
if (!normalizedTitle)
|
|
124
|
+
return null;
|
|
125
|
+
for (const section of PROFILE_SECTION_DEFINITIONS) {
|
|
126
|
+
if (section.aliases.some((alias) => alias.test(normalizedTitle))) {
|
|
127
|
+
return section.title;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
if (/additional notes/i.test(normalizedTitle) || /补充说明|附加说明/.test(normalizedTitle)) {
|
|
131
|
+
return PROFILE_ADDITIONAL_NOTES;
|
|
132
|
+
}
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
function parseProfileMetadataKey(rawKey) {
|
|
136
|
+
const normalized = rawKey.trim().toLowerCase();
|
|
137
|
+
if (normalized === 'updated'
|
|
138
|
+
|| normalized === 'last updated'
|
|
139
|
+
|| normalized === 'update'
|
|
140
|
+
|| normalized === 'date'
|
|
141
|
+
|| normalized === '更新时间'
|
|
142
|
+
|| normalized === '更新'
|
|
143
|
+
|| normalized === '日期') {
|
|
144
|
+
return 'updated';
|
|
145
|
+
}
|
|
146
|
+
if (normalized === 'evidence'
|
|
147
|
+
|| normalized === 'source'
|
|
148
|
+
|| normalized === 'proof'
|
|
149
|
+
|| normalized === 'reason'
|
|
150
|
+
|| normalized === '依据'
|
|
151
|
+
|| normalized === '证据'
|
|
152
|
+
|| normalized === '来源'
|
|
153
|
+
|| normalized === '理由') {
|
|
154
|
+
return 'evidence';
|
|
155
|
+
}
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
function parseProfileMetadataLine(rawLine) {
|
|
159
|
+
const trimmed = rawLine.trim().replace(/^[-*]\s*/, '');
|
|
160
|
+
const match = trimmed.match(/^([^::]+)\s*[::]\s*(.+)$/);
|
|
161
|
+
if (!match) {
|
|
162
|
+
return null;
|
|
163
|
+
}
|
|
164
|
+
const key = parseProfileMetadataKey(match[1]);
|
|
165
|
+
const value = match[2].trim();
|
|
166
|
+
if (!value) {
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
if (key) {
|
|
170
|
+
return { key, value };
|
|
171
|
+
}
|
|
172
|
+
return {
|
|
173
|
+
key: 'extra',
|
|
174
|
+
value: `${match[1].trim()}: ${value}`,
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
function parseInlineProfileMetadata(rawText) {
|
|
178
|
+
const extras = [];
|
|
179
|
+
let updated;
|
|
180
|
+
let evidence;
|
|
181
|
+
let signal = rawText.trim();
|
|
182
|
+
const inlineMatch = signal.match(/\s+\[([^\]]+)\]\s*$/);
|
|
183
|
+
if (!inlineMatch || inlineMatch.index === undefined) {
|
|
184
|
+
return { signal, updated, evidence, extras };
|
|
185
|
+
}
|
|
186
|
+
const metadataText = inlineMatch[1].trim();
|
|
187
|
+
const parts = metadataText
|
|
188
|
+
.split(/[;;]+/)
|
|
189
|
+
.map((part) => part.trim())
|
|
190
|
+
.filter(Boolean);
|
|
191
|
+
let parsedCount = 0;
|
|
192
|
+
for (const part of parts) {
|
|
193
|
+
const parsed = parseProfileMetadataLine(part);
|
|
194
|
+
if (!parsed) {
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
parsedCount += 1;
|
|
198
|
+
if (parsed.key === 'updated') {
|
|
199
|
+
updated = parsed.value;
|
|
200
|
+
}
|
|
201
|
+
else if (parsed.key === 'evidence') {
|
|
202
|
+
evidence = parsed.value;
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
extras.push(parsed.value);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
if (parsedCount === 0) {
|
|
209
|
+
return { signal, updated, evidence, extras };
|
|
210
|
+
}
|
|
211
|
+
signal = signal.slice(0, inlineMatch.index).trim();
|
|
212
|
+
return { signal, updated, evidence, extras };
|
|
213
|
+
}
|
|
214
|
+
function normalizeProfileSignalForRender(signal) {
|
|
215
|
+
return signal
|
|
216
|
+
.replace(/\s+/g, ' ')
|
|
217
|
+
.replace(/[ \t]+$/g, '')
|
|
218
|
+
.trim();
|
|
219
|
+
}
|
|
220
|
+
function normalizeProfileComparisonText(signal) {
|
|
221
|
+
return signal
|
|
222
|
+
.toLowerCase()
|
|
223
|
+
.replace(/\b(the user|user|they|them)\b/g, ' ')
|
|
224
|
+
.replace(/\b(prefers?|likes?|wants?|needs?|values?|cares about|dislikes?|hates?|avoids?|gets frustrated when)\b/g, ' ')
|
|
225
|
+
.replace(/喜欢|偏好|更喜欢|希望|想要|需要|重视|在乎|讨厌|反感|避免|最烦|用户|对方/g, ' ')
|
|
226
|
+
.replace(/[^a-z0-9\u4e00-\u9fff]+/g, ' ')
|
|
227
|
+
.replace(/\s+/g, ' ')
|
|
228
|
+
.trim();
|
|
229
|
+
}
|
|
230
|
+
function dedupeProfileExtras(extras) {
|
|
231
|
+
const seen = new Set();
|
|
232
|
+
const result = [];
|
|
233
|
+
for (const extra of extras) {
|
|
234
|
+
const normalized = extra.trim();
|
|
235
|
+
if (!normalized) {
|
|
236
|
+
continue;
|
|
237
|
+
}
|
|
238
|
+
const key = normalized.toLowerCase();
|
|
239
|
+
if (seen.has(key)) {
|
|
240
|
+
continue;
|
|
241
|
+
}
|
|
242
|
+
seen.add(key);
|
|
243
|
+
result.push(normalized);
|
|
244
|
+
}
|
|
245
|
+
return result;
|
|
246
|
+
}
|
|
247
|
+
function isExplicitProfileCorrection(signal) {
|
|
248
|
+
return (/\b(no longer|instead|rather than|not .* but|wrong|corrected?|actually|do not|does not|don't|doesn't)\b/i.test(signal)
|
|
249
|
+
|| /不是|不再|而不是|别再|不要|并非|纠正|改成|其实|不是想要|不想要/.test(signal));
|
|
250
|
+
}
|
|
251
|
+
function inferProfileTopicKey(signal) {
|
|
252
|
+
const normalized = normalizeProfileComparisonText(signal);
|
|
253
|
+
if (!normalized) {
|
|
254
|
+
return null;
|
|
255
|
+
}
|
|
256
|
+
const fieldMatch = signal.match(/^([^::]{2,40})[::]\s*.+$/);
|
|
257
|
+
if (fieldMatch) {
|
|
258
|
+
const fieldKey = normalizeProfileComparisonText(fieldMatch[1]);
|
|
259
|
+
if (fieldKey) {
|
|
260
|
+
return `field:${fieldKey}`;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
const topicPatterns = [
|
|
264
|
+
{ key: 'language', patterns: [/\blanguage\b/i, /中文|英文|双语|语言/] },
|
|
265
|
+
{ key: 'name', patterns: [/\bname\b/i, /名字|姓名|我叫/] },
|
|
266
|
+
{ key: 'role', patterns: [/\brole\b|\btitle\b|\bjob\b|\bbackground\b/i, /角色|职位|身份|背景|工程师|产品经理|创始人/] },
|
|
267
|
+
{ key: 'timezone', patterns: [/\btimezone\b/i, /时区/] },
|
|
268
|
+
{ key: 'verbosity', patterns: [/\bconcise\b|\bbrief\b|\bshort\b|\blong-winded\b|\bverbose\b|\bdetailed\b|\blengthy\b|\bexplanations?\b/i, /简洁|简短|啰嗦|冗长|别太长|长篇|详细|展开讲/] },
|
|
269
|
+
{ key: 'tone', patterns: [/\bdirect\b|\bcorporate\b|\btone\b|\bwording\b|\bpolite\b|\bblunt\b/i, /直接|官腔|语气|措辞|表达风格|说话方式/] },
|
|
270
|
+
{ key: 'corrections', patterns: [/\bcorrection\b|\bremember\b|\bforgotten\b/i, /纠正|记住|忘记|反复纠正/] },
|
|
271
|
+
{ key: 'proactivity', patterns: [/\bproactive\b|\binitiative\b/i, /主动|前置|提前|预判/] },
|
|
272
|
+
{ key: 'testing', patterns: [/\btests?\b|\btesting\b/i, /测试|提测|回归/] },
|
|
273
|
+
{ key: 'planning', patterns: [/\bplanning?\b|\bplan\b/i, /计划|方案|先.*后.*/] },
|
|
274
|
+
{ key: 'collaboration', patterns: [/\bcollaborat/i, /协作|合作|同伴|搭子/] },
|
|
275
|
+
{ key: 'scope', patterns: [/\bportrait\b|\bappearance\b|\bpersonality\b/i, /画像|长相|性格|特点/] },
|
|
276
|
+
{ key: 'values', patterns: [/\bvalue\b|\bmotivation\b|\bgoal\b|\bprinciple\b/i, /价值|动机|目标|原则|在乎|重视/] },
|
|
277
|
+
];
|
|
278
|
+
for (const topic of topicPatterns) {
|
|
279
|
+
if (topic.patterns.some((pattern) => pattern.test(signal) || pattern.test(normalized))) {
|
|
280
|
+
return topic.key;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
return null;
|
|
284
|
+
}
|
|
285
|
+
function isSingleValueProfileTopic(topicKey) {
|
|
286
|
+
if (!topicKey) {
|
|
287
|
+
return false;
|
|
288
|
+
}
|
|
289
|
+
return topicKey.startsWith('field:')
|
|
290
|
+
|| topicKey === 'language'
|
|
291
|
+
|| topicKey === 'name'
|
|
292
|
+
|| topicKey === 'role'
|
|
293
|
+
|| topicKey === 'timezone'
|
|
294
|
+
|| topicKey === 'scope';
|
|
295
|
+
}
|
|
296
|
+
function countProfileEntryMetadata(entry) {
|
|
297
|
+
return (entry.updated ? 1 : 0) + (entry.evidence ? 1 : 0) + entry.extras.length;
|
|
298
|
+
}
|
|
299
|
+
function compareProfileDates(left, right) {
|
|
300
|
+
if (!left && !right) {
|
|
301
|
+
return 0;
|
|
302
|
+
}
|
|
303
|
+
if (!left) {
|
|
304
|
+
return -1;
|
|
305
|
+
}
|
|
306
|
+
if (!right) {
|
|
307
|
+
return 1;
|
|
308
|
+
}
|
|
309
|
+
const leftTime = Date.parse(left);
|
|
310
|
+
const rightTime = Date.parse(right);
|
|
311
|
+
if (Number.isNaN(leftTime) || Number.isNaN(rightTime)) {
|
|
312
|
+
return left.localeCompare(right);
|
|
313
|
+
}
|
|
314
|
+
if (leftTime === rightTime) {
|
|
315
|
+
return 0;
|
|
316
|
+
}
|
|
317
|
+
return leftTime > rightTime ? 1 : -1;
|
|
318
|
+
}
|
|
319
|
+
function choosePreferredProfileEntry(left, right) {
|
|
320
|
+
const dateCompare = compareProfileDates(left.updated, right.updated);
|
|
321
|
+
if (dateCompare !== 0) {
|
|
322
|
+
return dateCompare > 0 ? left : right;
|
|
323
|
+
}
|
|
324
|
+
if (left.correction !== right.correction) {
|
|
325
|
+
return left.correction ? left : right;
|
|
326
|
+
}
|
|
327
|
+
const metadataDelta = countProfileEntryMetadata(left) - countProfileEntryMetadata(right);
|
|
328
|
+
if (metadataDelta !== 0) {
|
|
329
|
+
return metadataDelta > 0 ? left : right;
|
|
330
|
+
}
|
|
331
|
+
if (left.signal.length !== right.signal.length) {
|
|
332
|
+
return left.signal.length > right.signal.length ? left : right;
|
|
333
|
+
}
|
|
334
|
+
return left.order >= right.order ? left : right;
|
|
335
|
+
}
|
|
336
|
+
function areProfileSignalsSimilar(left, right) {
|
|
337
|
+
const leftText = normalizeProfileComparisonText(left.signal);
|
|
338
|
+
const rightText = normalizeProfileComparisonText(right.signal);
|
|
339
|
+
if (!leftText || !rightText) {
|
|
340
|
+
return false;
|
|
341
|
+
}
|
|
342
|
+
if (leftText === rightText) {
|
|
343
|
+
return true;
|
|
344
|
+
}
|
|
345
|
+
if (leftText.length >= 6 && rightText.includes(leftText)) {
|
|
346
|
+
return true;
|
|
347
|
+
}
|
|
348
|
+
if (rightText.length >= 6 && leftText.includes(rightText)) {
|
|
349
|
+
return true;
|
|
350
|
+
}
|
|
351
|
+
if (left.topicKey && left.topicKey === right.topicKey) {
|
|
352
|
+
const leftWords = new Set(leftText.split(' ').filter(Boolean));
|
|
353
|
+
const rightWords = new Set(rightText.split(' ').filter(Boolean));
|
|
354
|
+
if (leftWords.size > 0 && rightWords.size > 0) {
|
|
355
|
+
const overlap = [...leftWords].filter((word) => rightWords.has(word)).length;
|
|
356
|
+
const ratio = overlap / Math.min(leftWords.size, rightWords.size);
|
|
357
|
+
if (ratio >= 0.6) {
|
|
358
|
+
return true;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
return false;
|
|
363
|
+
}
|
|
364
|
+
function mergeProfileEntries(left, right) {
|
|
365
|
+
const preferred = choosePreferredProfileEntry(left, right);
|
|
366
|
+
const fallback = preferred === left ? right : left;
|
|
367
|
+
return {
|
|
368
|
+
signal: preferred.signal,
|
|
369
|
+
updated: compareProfileDates(left.updated, right.updated) >= 0 ? left.updated ?? right.updated : right.updated ?? left.updated,
|
|
370
|
+
evidence: preferred.evidence ?? fallback.evidence,
|
|
371
|
+
extras: dedupeProfileExtras([...left.extras, ...right.extras]),
|
|
372
|
+
topicKey: preferred.topicKey ?? fallback.topicKey,
|
|
373
|
+
correction: left.correction || right.correction,
|
|
374
|
+
order: Math.max(left.order, right.order),
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
function renderProfileEntry(entry) {
|
|
378
|
+
const metadataParts = [];
|
|
379
|
+
if (entry.updated) {
|
|
380
|
+
metadataParts.push(`updated: ${entry.updated}`);
|
|
381
|
+
}
|
|
382
|
+
if (entry.evidence) {
|
|
383
|
+
metadataParts.push(`evidence: ${entry.evidence}`);
|
|
384
|
+
}
|
|
385
|
+
metadataParts.push(...dedupeProfileExtras(entry.extras));
|
|
386
|
+
return metadataParts.length > 0
|
|
387
|
+
? `- ${entry.signal} [${metadataParts.join('; ')}]`
|
|
388
|
+
: `- ${entry.signal}`;
|
|
389
|
+
}
|
|
390
|
+
function parseProfileEntryBlock(blockLines, order) {
|
|
391
|
+
if (blockLines.length === 0) {
|
|
392
|
+
return null;
|
|
393
|
+
}
|
|
394
|
+
const [firstLine, ...restLines] = blockLines;
|
|
395
|
+
const firstText = firstLine.trim().replace(/^[-*]\s*/, '');
|
|
396
|
+
const inlineParsed = parseInlineProfileMetadata(firstText);
|
|
397
|
+
const signalParts = [inlineParsed.signal];
|
|
398
|
+
let updated = inlineParsed.updated;
|
|
399
|
+
let evidence = inlineParsed.evidence;
|
|
400
|
+
const extras = [...inlineParsed.extras];
|
|
401
|
+
for (const rawLine of restLines) {
|
|
402
|
+
const metadata = parseProfileMetadataLine(rawLine);
|
|
403
|
+
if (metadata) {
|
|
404
|
+
if (metadata.key === 'updated') {
|
|
405
|
+
updated = metadata.value;
|
|
406
|
+
}
|
|
407
|
+
else if (metadata.key === 'evidence') {
|
|
408
|
+
evidence = metadata.value;
|
|
409
|
+
}
|
|
410
|
+
else {
|
|
411
|
+
extras.push(metadata.value);
|
|
412
|
+
}
|
|
413
|
+
continue;
|
|
414
|
+
}
|
|
415
|
+
const continuation = rawLine.trim();
|
|
416
|
+
if (continuation) {
|
|
417
|
+
signalParts.push(continuation);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
const signal = normalizeProfileSignalForRender(signalParts.join(' '));
|
|
421
|
+
if (!signal) {
|
|
422
|
+
return null;
|
|
423
|
+
}
|
|
424
|
+
return {
|
|
425
|
+
signal,
|
|
426
|
+
updated,
|
|
427
|
+
evidence,
|
|
428
|
+
extras: dedupeProfileExtras(extras),
|
|
429
|
+
topicKey: inferProfileTopicKey(signal),
|
|
430
|
+
correction: isExplicitProfileCorrection(signal),
|
|
431
|
+
order,
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
function parseProfileSectionEntries(lines) {
|
|
435
|
+
const entries = [];
|
|
436
|
+
let currentBlock = [];
|
|
437
|
+
let order = 0;
|
|
438
|
+
const flush = () => {
|
|
439
|
+
const entry = parseProfileEntryBlock(currentBlock, order++);
|
|
440
|
+
if (entry) {
|
|
441
|
+
entries.push(entry);
|
|
442
|
+
}
|
|
443
|
+
currentBlock = [];
|
|
444
|
+
};
|
|
445
|
+
for (const rawLine of lines) {
|
|
446
|
+
if (rawLine.trim() === '') {
|
|
447
|
+
flush();
|
|
448
|
+
continue;
|
|
449
|
+
}
|
|
450
|
+
if (/^[-*]\s+/.test(rawLine)) {
|
|
451
|
+
flush();
|
|
452
|
+
currentBlock = [rawLine];
|
|
453
|
+
continue;
|
|
454
|
+
}
|
|
455
|
+
if (currentBlock.length === 0) {
|
|
456
|
+
currentBlock = [`- ${rawLine.trim()}`];
|
|
457
|
+
}
|
|
458
|
+
else {
|
|
459
|
+
currentBlock.push(rawLine);
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
flush();
|
|
463
|
+
return entries;
|
|
464
|
+
}
|
|
465
|
+
function normalizeProfileSectionEntries(lines) {
|
|
466
|
+
const parsedEntries = parseProfileSectionEntries(lines);
|
|
467
|
+
const mergedEntries = [];
|
|
468
|
+
for (const entry of parsedEntries) {
|
|
469
|
+
const existingIndex = mergedEntries.findIndex((existing) => {
|
|
470
|
+
if (normalizeProfileComparisonText(existing.signal) === normalizeProfileComparisonText(entry.signal)) {
|
|
471
|
+
return true;
|
|
472
|
+
}
|
|
473
|
+
if (existing.topicKey && entry.topicKey && existing.topicKey === entry.topicKey) {
|
|
474
|
+
return areProfileSignalsSimilar(existing, entry)
|
|
475
|
+
|| isSingleValueProfileTopic(entry.topicKey)
|
|
476
|
+
|| existing.correction
|
|
477
|
+
|| entry.correction;
|
|
478
|
+
}
|
|
479
|
+
return false;
|
|
480
|
+
});
|
|
481
|
+
if (existingIndex >= 0) {
|
|
482
|
+
mergedEntries[existingIndex] = mergeProfileEntries(mergedEntries[existingIndex], entry);
|
|
483
|
+
continue;
|
|
484
|
+
}
|
|
485
|
+
mergedEntries.push(entry);
|
|
486
|
+
}
|
|
487
|
+
return mergedEntries;
|
|
488
|
+
}
|
|
489
|
+
function stripInlineProfileMetadata(line) {
|
|
490
|
+
return parseInlineProfileMetadata(line).signal;
|
|
491
|
+
}
|
|
492
|
+
function routeLooseProfileLine(line) {
|
|
493
|
+
const normalized = stripInlineProfileMetadata(line.trim().replace(/^[-*]\s*/, ''));
|
|
494
|
+
const lower = normalized.toLowerCase();
|
|
495
|
+
if (!normalized) {
|
|
496
|
+
return 'Stable Preferences';
|
|
497
|
+
}
|
|
498
|
+
if (/language|name|role|title|background|location|timezone|pronouns?|company/i.test(normalized)
|
|
499
|
+
|| /我叫|我是|身份|角色|语言|地区|时区/.test(normalized)) {
|
|
500
|
+
return 'Basic Info';
|
|
501
|
+
}
|
|
502
|
+
if (/communication|tone|wording|concise|verbosity|direct|polite|blunt|reply style/i.test(lower)
|
|
503
|
+
|| /沟通|语气|措辞|表达|官腔|简洁|直接|啰嗦|回复风格/.test(normalized)) {
|
|
504
|
+
return 'Communication Style';
|
|
505
|
+
}
|
|
506
|
+
if (/workflow|working|collaboration|review|iterate|testing|tests|commit|planning/i.test(lower)
|
|
507
|
+
|| /工作流|协作|反馈|迭代|测试|提测|先.*后.*|改动方式/.test(normalized)) {
|
|
508
|
+
return 'Working Style';
|
|
509
|
+
}
|
|
510
|
+
if (/hate|dislike|annoy|frustrat|trigger|cannot stand|avoid/i.test(lower)
|
|
511
|
+
|| /最烦|讨厌|反感|不满|踩雷|不能接受|很在意/.test(normalized)) {
|
|
512
|
+
return 'Decision Signals';
|
|
513
|
+
}
|
|
514
|
+
if (/goal|value|motivation|principle|care about|optimizing for/i.test(lower)
|
|
515
|
+
|| /目标|价值|动机|原则|在乎|重视/.test(normalized)) {
|
|
516
|
+
return 'Values & Motivations';
|
|
517
|
+
}
|
|
518
|
+
return 'Stable Preferences';
|
|
519
|
+
}
|
|
520
|
+
function tidySectionLines(lines) {
|
|
521
|
+
const trimmed = [...lines];
|
|
522
|
+
while (trimmed.length > 0 && trimmed[0].trim() === '') {
|
|
523
|
+
trimmed.shift();
|
|
524
|
+
}
|
|
525
|
+
while (trimmed.length > 0 && trimmed[trimmed.length - 1].trim() === '') {
|
|
526
|
+
trimmed.pop();
|
|
527
|
+
}
|
|
528
|
+
const result = [];
|
|
529
|
+
let previousWasBlank = false;
|
|
530
|
+
for (const line of trimmed) {
|
|
531
|
+
const sanitized = line.replace(/[ \t]+$/g, '');
|
|
532
|
+
const isBlank = sanitized.trim() === '';
|
|
533
|
+
if (isBlank && previousWasBlank) {
|
|
534
|
+
continue;
|
|
535
|
+
}
|
|
536
|
+
result.push(isBlank ? '' : sanitized);
|
|
537
|
+
previousWasBlank = isBlank;
|
|
538
|
+
}
|
|
539
|
+
return result;
|
|
540
|
+
}
|
|
541
|
+
function normalizeProfileNotebook(content) {
|
|
542
|
+
const normalizedContent = content.replace(/\r\n/g, '\n').trim();
|
|
543
|
+
if (!normalizedContent) {
|
|
544
|
+
return buildDefaultProfileTemplate();
|
|
545
|
+
}
|
|
546
|
+
const sectionMap = new Map();
|
|
547
|
+
for (const title of PROFILE_SECTION_ORDER) {
|
|
548
|
+
sectionMap.set(title, []);
|
|
549
|
+
}
|
|
550
|
+
sectionMap.set(PROFILE_ADDITIONAL_NOTES, []);
|
|
551
|
+
let currentSection = null;
|
|
552
|
+
for (const line of normalizedContent.split('\n')) {
|
|
553
|
+
const trimmedLine = line.trim();
|
|
554
|
+
if (/^#\s+/.test(trimmedLine)) {
|
|
555
|
+
continue;
|
|
556
|
+
}
|
|
557
|
+
const sectionMatch = trimmedLine.match(/^##\s+(.+)$/);
|
|
558
|
+
if (sectionMatch) {
|
|
559
|
+
currentSection = canonicalizeProfileSectionTitle(sectionMatch[1]) ?? PROFILE_ADDITIONAL_NOTES;
|
|
560
|
+
continue;
|
|
561
|
+
}
|
|
562
|
+
if (!trimmedLine && currentSection === null) {
|
|
563
|
+
continue;
|
|
564
|
+
}
|
|
565
|
+
const targetSection = currentSection ?? routeLooseProfileLine(line);
|
|
566
|
+
const bucket = sectionMap.get(targetSection) ?? [];
|
|
567
|
+
bucket.push(line);
|
|
568
|
+
sectionMap.set(targetSection, bucket);
|
|
569
|
+
}
|
|
570
|
+
const output = ['# User Profile', ''];
|
|
571
|
+
const normalizedSections = new Map();
|
|
572
|
+
for (const title of PROFILE_SECTION_ORDER) {
|
|
573
|
+
normalizedSections.set(title, normalizeProfileSectionEntries(sectionMap.get(title) ?? []));
|
|
574
|
+
}
|
|
575
|
+
const resolvedTopics = new Set();
|
|
576
|
+
for (const title of PROFILE_SECTION_ORDER) {
|
|
577
|
+
if (title === 'Do Not Assume / Open Questions') {
|
|
578
|
+
continue;
|
|
579
|
+
}
|
|
580
|
+
for (const entry of normalizedSections.get(title) ?? []) {
|
|
581
|
+
if (entry.topicKey) {
|
|
582
|
+
resolvedTopics.add(entry.topicKey);
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
const openQuestions = normalizedSections.get('Do Not Assume / Open Questions') ?? [];
|
|
587
|
+
normalizedSections.set('Do Not Assume / Open Questions', openQuestions.filter((entry) => !entry.topicKey || !resolvedTopics.has(entry.topicKey)));
|
|
588
|
+
for (const title of PROFILE_SECTION_ORDER) {
|
|
589
|
+
output.push(`## ${title}`);
|
|
590
|
+
const sectionLines = (normalizedSections.get(title) ?? []).map((entry) => renderProfileEntry(entry));
|
|
591
|
+
if (sectionLines.length > 0) {
|
|
592
|
+
output.push(...sectionLines);
|
|
593
|
+
}
|
|
594
|
+
output.push('');
|
|
595
|
+
}
|
|
596
|
+
const additionalNotes = tidySectionLines(sectionMap.get(PROFILE_ADDITIONAL_NOTES) ?? []);
|
|
597
|
+
if (additionalNotes.length > 0) {
|
|
598
|
+
output.push(`## ${PROFILE_ADDITIONAL_NOTES}`);
|
|
599
|
+
output.push(...additionalNotes);
|
|
600
|
+
output.push('');
|
|
601
|
+
}
|
|
602
|
+
return output.join('\n').trim();
|
|
603
|
+
}
|
|
604
|
+
function normalizeNotebookContent(type, content) {
|
|
605
|
+
if (type === 'profile') {
|
|
606
|
+
return normalizeProfileNotebook(content);
|
|
607
|
+
}
|
|
608
|
+
return content;
|
|
609
|
+
}
|
|
69
610
|
// ============================================================================
|
|
70
611
|
// Default Templates
|
|
71
612
|
// ============================================================================
|
|
@@ -106,15 +647,8 @@ const DEFAULT_EXPERIENCE = `# Experience Notebook
|
|
|
106
647
|
- SelfEvolve restart kills all background Bash tasks
|
|
107
648
|
- Basic sensing capabilities should not be guarded by feature flags
|
|
108
649
|
`;
|
|
109
|
-
/** Default profile notebook —
|
|
110
|
-
const DEFAULT_PROFILE =
|
|
111
|
-
|
|
112
|
-
## Basic Info
|
|
113
|
-
- Language preference: (auto-detected)
|
|
114
|
-
|
|
115
|
-
## Communication Preferences
|
|
116
|
-
- (The AI will learn your preferences over time and update this notebook)
|
|
117
|
-
`;
|
|
650
|
+
/** Default profile notebook — structured user model */
|
|
651
|
+
const DEFAULT_PROFILE = buildDefaultProfileTemplate();
|
|
118
652
|
// ============================================================================
|
|
119
653
|
// NotebookManager
|
|
120
654
|
// ============================================================================
|
|
@@ -185,19 +719,31 @@ export class NotebookManager {
|
|
|
185
719
|
try {
|
|
186
720
|
this.migrateLegacyProjectLocalNotebook(type);
|
|
187
721
|
if (fs.existsSync(filePath)) {
|
|
188
|
-
|
|
722
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
723
|
+
const normalized = normalizeNotebookContent(type, content);
|
|
724
|
+
if (normalized !== content) {
|
|
725
|
+
try {
|
|
726
|
+
ensureDir(path.dirname(filePath));
|
|
727
|
+
fs.writeFileSync(filePath, normalized, 'utf-8');
|
|
728
|
+
}
|
|
729
|
+
catch {
|
|
730
|
+
// 归一化写回失败时,至少返回归一化后的内容给调用方
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
return normalized;
|
|
189
734
|
}
|
|
190
735
|
// Auto-initialize: try bundled default-memory/ first, then fallback to hardcoded template
|
|
191
736
|
const defaultContent = this.getBundledOrDefault(type);
|
|
192
737
|
if (defaultContent) {
|
|
738
|
+
const normalizedDefault = normalizeNotebookContent(type, defaultContent);
|
|
193
739
|
try {
|
|
194
740
|
ensureDir(path.dirname(filePath));
|
|
195
|
-
fs.writeFileSync(filePath,
|
|
196
|
-
return
|
|
741
|
+
fs.writeFileSync(filePath, normalizedDefault, 'utf-8');
|
|
742
|
+
return normalizedDefault;
|
|
197
743
|
}
|
|
198
744
|
catch {
|
|
199
745
|
// Non-fatal: return the template content even if file write fails
|
|
200
|
-
return
|
|
746
|
+
return normalizedDefault;
|
|
201
747
|
}
|
|
202
748
|
}
|
|
203
749
|
}
|
|
@@ -234,7 +780,8 @@ export class NotebookManager {
|
|
|
234
780
|
write(type, content) {
|
|
235
781
|
const filePath = this.getPath(type);
|
|
236
782
|
const maxTokens = MAX_TOKENS[type];
|
|
237
|
-
const
|
|
783
|
+
const normalizedContent = normalizeNotebookContent(type, content);
|
|
784
|
+
const tokens = estimateTokens(normalizedContent);
|
|
238
785
|
this.migrateLegacyProjectLocalNotebook(type);
|
|
239
786
|
if (tokens > maxTokens) {
|
|
240
787
|
return {
|
|
@@ -248,7 +795,7 @@ export class NotebookManager {
|
|
|
248
795
|
ensureDir(path.dirname(filePath));
|
|
249
796
|
// 原子写入:先写临时文件再 rename,防止进程崩溃导致文件损坏
|
|
250
797
|
const tmpPath = filePath + '.tmp';
|
|
251
|
-
fs.writeFileSync(tmpPath,
|
|
798
|
+
fs.writeFileSync(tmpPath, normalizedContent, 'utf-8');
|
|
252
799
|
fs.renameSync(tmpPath, filePath);
|
|
253
800
|
return { success: true, tokens, path: filePath };
|
|
254
801
|
}
|