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.
Files changed (189) hide show
  1. package/dist/config/axon-md-parser.d.ts.map +1 -1
  2. package/dist/config/axon-md-parser.js +21 -4
  3. package/dist/config/axon-md-parser.js.map +1 -1
  4. package/dist/config/index.d.ts +9 -6
  5. package/dist/config/index.d.ts.map +1 -1
  6. package/dist/config/index.js +1 -0
  7. package/dist/config/index.js.map +1 -1
  8. package/dist/context/enhanced.d.ts.map +1 -1
  9. package/dist/context/enhanced.js +5 -0
  10. package/dist/context/enhanced.js.map +1 -1
  11. package/dist/core/loop.d.ts +45 -1
  12. package/dist/core/loop.d.ts.map +1 -1
  13. package/dist/core/loop.js +317 -111
  14. package/dist/core/loop.js.map +1 -1
  15. package/dist/core/max-tokens.d.ts.map +1 -1
  16. package/dist/core/max-tokens.js +5 -0
  17. package/dist/core/max-tokens.js.map +1 -1
  18. package/dist/core/session.d.ts.map +1 -1
  19. package/dist/core/session.js +7 -14
  20. package/dist/core/session.js.map +1 -1
  21. package/dist/daemon/config.d.ts +6 -6
  22. package/dist/daemon/executor.d.ts.map +1 -1
  23. package/dist/daemon/executor.js +24 -30
  24. package/dist/daemon/executor.js.map +1 -1
  25. package/dist/mcp/config.d.ts +8 -8
  26. package/dist/media/index.d.ts +1 -1
  27. package/dist/media/index.d.ts.map +1 -1
  28. package/dist/media/index.js +3 -3
  29. package/dist/media/index.js.map +1 -1
  30. package/dist/media/office.d.ts +17 -0
  31. package/dist/media/office.d.ts.map +1 -1
  32. package/dist/media/office.js +227 -2
  33. package/dist/media/office.js.map +1 -1
  34. package/dist/memory/memory-search.d.ts +4 -1
  35. package/dist/memory/memory-search.d.ts.map +1 -1
  36. package/dist/memory/memory-search.js +31 -2
  37. package/dist/memory/memory-search.js.map +1 -1
  38. package/dist/memory/memory-sync.d.ts +7 -0
  39. package/dist/memory/memory-sync.d.ts.map +1 -1
  40. package/dist/memory/memory-sync.js +88 -0
  41. package/dist/memory/memory-sync.js.map +1 -1
  42. package/dist/memory/notebook.d.ts.map +1 -1
  43. package/dist/memory/notebook.js +562 -15
  44. package/dist/memory/notebook.js.map +1 -1
  45. package/dist/models/config.d.ts.map +1 -1
  46. package/dist/models/config.js +7 -4
  47. package/dist/models/config.js.map +1 -1
  48. package/dist/models/index.d.ts +1 -0
  49. package/dist/models/index.d.ts.map +1 -1
  50. package/dist/models/index.js +1 -0
  51. package/dist/models/index.js.map +1 -1
  52. package/dist/models/model-limits.d.ts +13 -0
  53. package/dist/models/model-limits.d.ts.map +1 -0
  54. package/dist/models/model-limits.js +79 -0
  55. package/dist/models/model-limits.js.map +1 -0
  56. package/dist/prompt/attachments.d.ts +1 -0
  57. package/dist/prompt/attachments.d.ts.map +1 -1
  58. package/dist/prompt/attachments.js +17 -2
  59. package/dist/prompt/attachments.js.map +1 -1
  60. package/dist/prompt/cache.d.ts +1 -8
  61. package/dist/prompt/cache.d.ts.map +1 -1
  62. package/dist/prompt/cache.js +37 -9
  63. package/dist/prompt/cache.js.map +1 -1
  64. package/dist/session/index.d.ts +1 -0
  65. package/dist/session/index.d.ts.map +1 -1
  66. package/dist/session/index.js.map +1 -1
  67. package/dist/tools/agent.d.ts +8 -0
  68. package/dist/tools/agent.d.ts.map +1 -1
  69. package/dist/tools/agent.js +66 -2
  70. package/dist/tools/agent.js.map +1 -1
  71. package/dist/tools/file.d.ts.map +1 -1
  72. package/dist/tools/file.js +99 -30
  73. package/dist/tools/file.js.map +1 -1
  74. package/dist/tools/index.d.ts.map +1 -1
  75. package/dist/tools/index.js +7 -4
  76. package/dist/tools/index.js.map +1 -1
  77. package/dist/tools/mcp.d.ts.map +1 -1
  78. package/dist/tools/mcp.js +5 -0
  79. package/dist/tools/mcp.js.map +1 -1
  80. package/dist/tools/memory-search.d.ts +1 -1
  81. package/dist/tools/memory-search.d.ts.map +1 -1
  82. package/dist/tools/memory-search.js +1 -1
  83. package/dist/tools/memory-search.js.map +1 -1
  84. package/dist/tools/notebook-write.d.ts.map +1 -1
  85. package/dist/tools/notebook-write.js +3 -1
  86. package/dist/tools/notebook-write.js.map +1 -1
  87. package/dist/tools/skill.d.ts +0 -6
  88. package/dist/tools/skill.d.ts.map +1 -1
  89. package/dist/tools/skill.js +211 -34
  90. package/dist/tools/skill.js.map +1 -1
  91. package/dist/types/messages.d.ts +1 -5
  92. package/dist/types/messages.d.ts.map +1 -1
  93. package/dist/web/server/api-manager.d.ts +7 -5
  94. package/dist/web/server/api-manager.d.ts.map +1 -1
  95. package/dist/web/server/api-manager.js +140 -146
  96. package/dist/web/server/api-manager.js.map +1 -1
  97. package/dist/web/server/conversation.d.ts.map +1 -1
  98. package/dist/web/server/conversation.js +27 -10
  99. package/dist/web/server/conversation.js.map +1 -1
  100. package/dist/web/server/routes/api.d.ts.map +1 -1
  101. package/dist/web/server/routes/api.js +21 -24
  102. package/dist/web/server/routes/api.js.map +1 -1
  103. package/dist/web/server/routes/axon-cloud.d.ts.map +1 -1
  104. package/dist/web/server/routes/axon-cloud.js +350 -1
  105. package/dist/web/server/routes/axon-cloud.js.map +1 -1
  106. package/dist/web/server/routes/config-api.d.ts.map +1 -1
  107. package/dist/web/server/routes/config-api.js +12 -20
  108. package/dist/web/server/routes/config-api.js.map +1 -1
  109. package/dist/web/server/routes/download-proxy.d.ts.map +1 -1
  110. package/dist/web/server/routes/download-proxy.js +234 -12
  111. package/dist/web/server/routes/download-proxy.js.map +1 -1
  112. package/dist/web/server/runtime/api-connection-test.d.ts +18 -0
  113. package/dist/web/server/runtime/api-connection-test.d.ts.map +1 -0
  114. package/dist/web/server/runtime/api-connection-test.js +62 -0
  115. package/dist/web/server/runtime/api-connection-test.js.map +1 -0
  116. package/dist/web/server/runtime/codex-client.d.ts +5 -0
  117. package/dist/web/server/runtime/codex-client.d.ts.map +1 -1
  118. package/dist/web/server/runtime/codex-client.js +229 -29
  119. package/dist/web/server/runtime/codex-client.js.map +1 -1
  120. package/dist/web/server/runtime/runtime-model-catalog.d.ts +10 -0
  121. package/dist/web/server/runtime/runtime-model-catalog.d.ts.map +1 -0
  122. package/dist/web/server/runtime/runtime-model-catalog.js +85 -0
  123. package/dist/web/server/runtime/runtime-model-catalog.js.map +1 -0
  124. package/dist/web/server/runtime/runtime-model-list.d.ts +24 -0
  125. package/dist/web/server/runtime/runtime-model-list.d.ts.map +1 -0
  126. package/dist/web/server/runtime/runtime-model-list.js +43 -0
  127. package/dist/web/server/runtime/runtime-model-list.js.map +1 -0
  128. package/dist/web/server/runtime/runtime-selection.d.ts +1 -0
  129. package/dist/web/server/runtime/runtime-selection.d.ts.map +1 -1
  130. package/dist/web/server/runtime/runtime-selection.js +5 -3
  131. package/dist/web/server/runtime/runtime-selection.js.map +1 -1
  132. package/dist/web/server/runtime/types.d.ts +5 -0
  133. package/dist/web/server/runtime/types.d.ts.map +1 -1
  134. package/dist/web/server/runtime/utility-client.d.ts.map +1 -1
  135. package/dist/web/server/runtime/utility-client.js +4 -1
  136. package/dist/web/server/runtime/utility-client.js.map +1 -1
  137. package/dist/web/server/services/axon-cloud-service.d.ts +24 -0
  138. package/dist/web/server/services/axon-cloud-service.d.ts.map +1 -1
  139. package/dist/web/server/services/axon-cloud-service.js +93 -0
  140. package/dist/web/server/services/axon-cloud-service.js.map +1 -1
  141. package/dist/web/server/services/config-service.d.ts +2 -1
  142. package/dist/web/server/services/config-service.d.ts.map +1 -1
  143. package/dist/web/server/services/config-service.js +28 -18
  144. package/dist/web/server/services/config-service.js.map +1 -1
  145. package/dist/web/server/session-manager.d.ts +9 -0
  146. package/dist/web/server/session-manager.d.ts.map +1 -1
  147. package/dist/web/server/session-manager.js +38 -0
  148. package/dist/web/server/session-manager.js.map +1 -1
  149. package/dist/web/server/slash-commands.d.ts.map +1 -1
  150. package/dist/web/server/slash-commands.js +79 -19
  151. package/dist/web/server/slash-commands.js.map +1 -1
  152. package/dist/web/server/task-manager.d.ts +4 -15
  153. package/dist/web/server/task-manager.d.ts.map +1 -1
  154. package/dist/web/server/task-manager.js +11 -4
  155. package/dist/web/server/task-manager.js.map +1 -1
  156. package/dist/web/server/web-auth.d.ts.map +1 -1
  157. package/dist/web/server/web-auth.js +32 -28
  158. package/dist/web/server/web-auth.js.map +1 -1
  159. package/dist/web/server/websocket.d.ts.map +1 -1
  160. package/dist/web/server/websocket.js +62 -35
  161. package/dist/web/server/websocket.js.map +1 -1
  162. package/dist/web/shared/auth-summary.d.ts.map +1 -1
  163. package/dist/web/shared/auth-summary.js +5 -2
  164. package/dist/web/shared/auth-summary.js.map +1 -1
  165. package/dist/web/shared/model-catalog.d.ts +3 -2
  166. package/dist/web/shared/model-catalog.d.ts.map +1 -1
  167. package/dist/web/shared/model-catalog.js +29 -37
  168. package/dist/web/shared/model-catalog.js.map +1 -1
  169. package/dist/web/shared/runtime-capabilities.d.ts +37 -0
  170. package/dist/web/shared/runtime-capabilities.d.ts.map +1 -0
  171. package/dist/web/shared/runtime-capabilities.js +101 -0
  172. package/dist/web/shared/runtime-capabilities.js.map +1 -0
  173. package/dist/web/shared/setup-runtime.d.ts +23 -0
  174. package/dist/web/shared/setup-runtime.d.ts.map +1 -1
  175. package/dist/web/shared/setup-runtime.js +119 -19
  176. package/dist/web/shared/setup-runtime.js.map +1 -1
  177. package/dist/web/shared/thinking-config.d.ts.map +1 -1
  178. package/dist/web/shared/thinking-config.js +10 -5
  179. package/dist/web/shared/thinking-config.js.map +1 -1
  180. package/dist/web/shared/types.d.ts +8 -0
  181. package/dist/web/shared/types.d.ts.map +1 -1
  182. package/dist/web/shared/types.js.map +1 -1
  183. package/electron/main.cjs +8 -1
  184. package/package.json +3 -1
  185. package/src/web/client/dist/assets/index-B2M5Nr-5.js +736 -0
  186. package/src/web/client/dist/assets/index-COEqamS-.css +32 -0
  187. package/src/web/client/dist/index.html +2 -2
  188. package/src/web/client/dist/assets/index-B0gwq5PJ.js +0 -727
  189. package/src/web/client/dist/assets/index-CwhuMLtk.css +0 -32
@@ -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 — minimal placeholder */
110
- const DEFAULT_PROFILE = `# User 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
- return fs.readFileSync(filePath, 'utf-8');
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, defaultContent, 'utf-8');
196
- return defaultContent;
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 defaultContent;
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 tokens = estimateTokens(content);
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, content, 'utf-8');
798
+ fs.writeFileSync(tmpPath, normalizedContent, 'utf-8');
252
799
  fs.renameSync(tmpPath, filePath);
253
800
  return { success: true, tokens, path: filePath };
254
801
  }