@miphamai/cli 0.27.0 → 0.29.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,616 @@
1
+ /**
2
+ * CRSI Phase 2: Auto-Reflection Engine
3
+ *
4
+ * Structured API for post-turn conversation reflection, CRSI pipeline
5
+ * integration, and persistent memory management. Builds on top of the
6
+ * Phase 1 rule-level self-improvement (PatternAnalyzer, RuleEngine,
7
+ * EffectivenessTracker) to enable code-level and session-level learning.
8
+ *
9
+ * Architecture:
10
+ * Turn completes → analyzeTurn() → persist() → CRSI pipeline feed
11
+ * AI does the thinking; this module provides storage + integration.
12
+ */
13
+
14
+ import { MemoryManager, type MemoryEntry } from './memory/memory-manager.js'
15
+ import type { PatternAnalyzer, Pattern } from '../agent/pattern-analyzer.js'
16
+ import type { ExperienceRuleEngine, ToolRule } from './rule-engine.js'
17
+ import type { EffectivenessTracker } from '../agent/effectiveness-tracker.js'
18
+ import { join } from 'node:path'
19
+ import { homedir } from 'node:os'
20
+
21
+ // ── Types ──
22
+
23
+ export interface ToolCallRecord {
24
+ /** Tool name (Bash, Read, Write, Edit, Agent, etc.) */
25
+ name: string
26
+ /** Tool input parameters */
27
+ input: Record<string, unknown>
28
+ /** Whether the tool executed successfully */
29
+ success: boolean
30
+ /** Error message if the tool failed */
31
+ error?: string
32
+ /** Duration in ms */
33
+ durationMs?: number
34
+ }
35
+
36
+ export interface CrsiInsight {
37
+ /** Category: timeout | tool-params | search | import | semantic */
38
+ category: string
39
+ /** Human-readable description of the insight */
40
+ description: string
41
+ /** Severity: 'info' | 'warning' | 'critical' */
42
+ severity: 'info' | 'warning' | 'critical'
43
+ /** Suggested action (e.g. "增加 Bash timeout 到 300000ms") */
44
+ suggestion: string
45
+ /** Evidence: concrete examples supporting this insight */
46
+ evidence: string[]
47
+ /** Whether this insight can be auto-applied as a CRSI rule */
48
+ autoApplicable: boolean
49
+ }
50
+
51
+ export interface TurnReflection {
52
+ /** Unique ID for this reflection */
53
+ id: string
54
+ /** Session ID */
55
+ sessionId: string
56
+ /** Timestamp */
57
+ timestamp: string
58
+ /** Summary of the turn (1-3 sentences) */
59
+ summary: string
60
+ /** What went well */
61
+ successes: string[]
62
+ /** What failed or could be improved */
63
+ failures: string[]
64
+ /** CRSI-specific insights for rule generation */
65
+ crsiInsights: CrsiInsight[]
66
+ /** Key decisions made during this turn */
67
+ decisions: string[]
68
+ /** Action items for future sessions */
69
+ actionItems: string[]
70
+ }
71
+
72
+ // ── Constants ──
73
+
74
+ const DEFAULT_MEMORY_DIR = join(homedir(), '.mipham', 'memory')
75
+
76
+ /** Minimum number of similar failures before a CRSI rule is generated. */
77
+ const CRSI_RULE_THRESHOLD = 2
78
+
79
+ /** How many past reflections to recall for context. */
80
+ const MAX_RECALL = 5
81
+
82
+ // ── Engine ──
83
+
84
+ export class AutoMemoryEngine {
85
+ private memoryManager: MemoryManager
86
+ private patternAnalyzer?: PatternAnalyzer
87
+ private ruleEngine?: ExperienceRuleEngine
88
+ private effectivenessTracker?: EffectivenessTracker
89
+
90
+ /** Accumulated reflections for the current session (not yet persisted to disk). */
91
+ private sessionReflections: TurnReflection[] = []
92
+
93
+ constructor(memoryDir: string = DEFAULT_MEMORY_DIR) {
94
+ this.memoryManager = new MemoryManager(memoryDir)
95
+ this.memoryManager.loadAll()
96
+ }
97
+
98
+ // ── CRSI Pipeline Wiring ──
99
+
100
+ /**
101
+ * Wire the CRSI Phase 1 pipeline into the auto-memory engine.
102
+ * Enables insights → rule generation and effectiveness tracking.
103
+ */
104
+ setCrsiPipeline(
105
+ patternAnalyzer: PatternAnalyzer,
106
+ ruleEngine: ExperienceRuleEngine,
107
+ effectivenessTracker: EffectivenessTracker,
108
+ ): void {
109
+ this.patternAnalyzer = patternAnalyzer
110
+ this.ruleEngine = ruleEngine
111
+ this.effectivenessTracker = effectivenessTracker
112
+ }
113
+
114
+ // ── Core API ──
115
+
116
+ /**
117
+ * Analyze a completed conversation turn and produce a structured reflection.
118
+ *
119
+ * The AI calls this after each response completes. The engine:
120
+ * 1. Analyzes tool call patterns (successes, failures, timeouts)
121
+ * 2. Cross-references with CRSI pipeline for rule suggestions
122
+ * 3. Produces a TurnReflection ready for persistence
123
+ */
124
+ analyzeTurn(params: {
125
+ sessionId: string
126
+ userMessage: string
127
+ assistantContent: string
128
+ toolCalls: ToolCallRecord[]
129
+ modelProvider: string
130
+ modelId: string
131
+ turnDurationMs: number
132
+ }): TurnReflection {
133
+ const { sessionId, userMessage, assistantContent, toolCalls, modelProvider, modelId } = params
134
+
135
+ // 1. Categorize tool calls
136
+ const successes: string[] = []
137
+ const failures: string[] = []
138
+ for (const tc of toolCalls) {
139
+ if (tc.success) {
140
+ successes.push(`${tc.name}: ${this.summarizeToolInput(tc)}`)
141
+ } else {
142
+ failures.push(`${tc.name}: ${tc.error || 'unknown error'} — ${this.summarizeToolInput(tc)}`)
143
+ }
144
+ }
145
+
146
+ // 2. Extract CRSI insights from tool failures
147
+ const crsiInsights = this.extractCrsiInsights(toolCalls)
148
+
149
+ // 3. Cross-reference with existing CRSI rules
150
+ this.crossReferenceWithRules(crsiInsights)
151
+
152
+ // 4. Analyze for decisions and action items
153
+ const decisions = this.extractDecisions(userMessage, assistantContent)
154
+ const actionItems = this.extractActionItems(assistantContent, toolCalls)
155
+
156
+ // 5. Build summary
157
+ const summary = this.buildSummary(successes, failures, crsiInsights, decisions)
158
+
159
+ const reflection: TurnReflection = {
160
+ id: `reflection-${sessionId}-${Date.now().toString(36)}`,
161
+ sessionId,
162
+ timestamp: new Date().toISOString(),
163
+ summary,
164
+ successes,
165
+ failures,
166
+ crsiInsights,
167
+ decisions,
168
+ actionItems,
169
+ }
170
+
171
+ this.sessionReflections.push(reflection)
172
+ return reflection
173
+ }
174
+
175
+ /**
176
+ * Persist a turn reflection to the memory store.
177
+ *
178
+ * Writes a structured .md file with frontmatter to ~/.mipham/memory/
179
+ * and updates the MEMORY.md index. CRSI insights that meet the threshold
180
+ * are fed into the rule engine for auto-rule generation.
181
+ */
182
+ persist(reflection: TurnReflection): void {
183
+ const content = this.formatReflectionContent(reflection)
184
+ const relevance = this.extractRelevance(reflection)
185
+
186
+ this.memoryManager.write(reflection.id, content, {
187
+ type: 'feedback',
188
+ relevance,
189
+ why: `CRSI Phase 2 自动复盘 — 会话 ${reflection.sessionId}`,
190
+ howToApply: '下次会话开始时回顾此文件,检查是否有可操作的改进项',
191
+ })
192
+
193
+ // Feed CRSI insights into the rule engine
194
+ this.feedCrsiPipeline(reflection)
195
+ }
196
+
197
+ /**
198
+ * Persist ALL accumulated session reflections at once (e.g. on session end).
199
+ */
200
+ persistAll(): void {
201
+ for (const reflection of this.sessionReflections) {
202
+ this.persist(reflection)
203
+ }
204
+
205
+ // Also write a session-level summary
206
+ if (this.sessionReflections.length > 0) {
207
+ this.writeSessionSummary()
208
+ }
209
+
210
+ // Flush CRSI state
211
+ if (this.effectivenessTracker) {
212
+ this.effectivenessTracker.persist()
213
+ }
214
+ }
215
+
216
+ /**
217
+ * Recall relevant past reflections for a given context.
218
+ */
219
+ recall(context: string, limit: number = MAX_RECALL): MemoryEntry[] {
220
+ return this.memoryManager.recall(context, limit)
221
+ }
222
+
223
+ /**
224
+ * Build a system reminder string from past reflections for injection
225
+ * into the next session's system prompt.
226
+ */
227
+ buildReminder(context: string, maxTokens?: number): string {
228
+ return this.memoryManager.buildSystemReminder(context, maxTokens)
229
+ }
230
+
231
+ /**
232
+ * Get the session reflection count (useful for stats).
233
+ */
234
+ get sessionReflectionCount(): number {
235
+ return this.sessionReflections.length
236
+ }
237
+
238
+ /**
239
+ * Get accumulated CRSI insights across all session reflections.
240
+ */
241
+ get accumulatedInsights(): CrsiInsight[] {
242
+ return this.sessionReflections.flatMap((r) => r.crsiInsights)
243
+ }
244
+
245
+ // ── Private: CRSI Integration ──
246
+
247
+ /**
248
+ * Extract CRSI insights from tool call records.
249
+ *
250
+ * Identifies patterns like:
251
+ * - timeout: heavy commands without explicit timeout
252
+ * - tool-params: git --force without sandbox disable
253
+ * - import: ESM imports missing .js extension (from Edit/Write errors)
254
+ * - search: full-repo Grep without directory scoping
255
+ */
256
+ private extractCrsiInsights(toolCalls: ToolCallRecord[]): CrsiInsight[] {
257
+ const insights: CrsiInsight[] = []
258
+ const seen = new Set<string>()
259
+
260
+ for (const tc of toolCalls) {
261
+ if (tc.success) continue
262
+
263
+ const category = this.categorizeFailure(tc)
264
+ const key = `${category}:${tc.name}`
265
+ if (seen.has(key)) continue
266
+ seen.add(key)
267
+
268
+ const evidence = this.gatherEvidence(tc, toolCalls)
269
+ const autoApplicable = ['timeout', 'tool-params'].includes(category)
270
+
271
+ const insight: CrsiInsight = {
272
+ category,
273
+ description: `${tc.name} 工具调用失败: ${tc.error || 'unknown'}`,
274
+ severity: tc.error?.includes('timeout') ? 'critical' : 'warning',
275
+ suggestion: this.suggestFix(category, tc),
276
+ evidence,
277
+ autoApplicable,
278
+ }
279
+
280
+ insights.push(insight)
281
+ }
282
+
283
+ return insights
284
+ }
285
+
286
+ /**
287
+ * Cross-reference new insights with existing CRSI rules.
288
+ * If a similar rule already exists, mark the insight as already-covered.
289
+ */
290
+ private crossReferenceWithRules(insights: CrsiInsight[]): void {
291
+ if (!this.ruleEngine) return
292
+
293
+ const activeRules = this.ruleEngine.getActiveRules()
294
+ for (const insight of insights) {
295
+ const covered = activeRules.some((r) => r.category === insight.category)
296
+ if (covered) {
297
+ insight.description += ' (已有对应 CRSI 规则覆盖)'
298
+ insight.autoApplicable = false // Don't create duplicate rules
299
+ }
300
+ }
301
+ }
302
+
303
+ /**
304
+ * Feed CRSI insights into the rule engine for automatic rule generation.
305
+ * Only insights that are auto-applicable and have sufficient evidence
306
+ * (≥ CRSI_RULE_THRESHOLD similar failures) are converted to rules.
307
+ */
308
+ private feedCrsiPipeline(reflection: TurnReflection): void {
309
+ if (!this.ruleEngine || !this.patternAnalyzer) return
310
+
311
+ // Count failures by category across ALL reflections (not just this one)
312
+ const categoryCounts = new Map<string, number>()
313
+ for (const r of this.sessionReflections) {
314
+ for (const insight of r.crsiInsights) {
315
+ if (!insight.autoApplicable) continue
316
+ const count = categoryCounts.get(insight.category) || 0
317
+ categoryCounts.set(insight.category, count + 1)
318
+ }
319
+ }
320
+
321
+ // Generate rules for categories that meet the threshold
322
+ for (const [category, count] of categoryCounts) {
323
+ if (count < CRSI_RULE_THRESHOLD) continue
324
+
325
+ // Use PatternAnalyzer to generate a proper ToolRule
326
+ const pattern: Pattern = {
327
+ id: `auto-${category}-${Date.now().toString(36)}`,
328
+ category: category as Pattern['category'],
329
+ agentName: 'auto-memory',
330
+ frequency: count,
331
+ confidence: count >= 5 ? 'high' : 'medium',
332
+ examples: this.sessionReflections
333
+ .flatMap((r) => r.crsiInsights)
334
+ .filter((i) => i.category === category)
335
+ .flatMap((i) => i.evidence)
336
+ .slice(0, 5),
337
+ firstSeen: this.sessionReflections[0]?.timestamp || '',
338
+ lastSeen: this.sessionReflections[this.sessionReflections.length - 1]?.timestamp || '',
339
+ }
340
+
341
+ const toolRule = this.patternAnalyzer.toToolRule(pattern)
342
+ this.ruleEngine.register(toolRule)
343
+
344
+ // Track the new rule's effectiveness
345
+ if (this.effectivenessTracker) {
346
+ this.effectivenessTracker.recordApplication(toolRule.id, true)
347
+ }
348
+ }
349
+ }
350
+
351
+ // ── Private: Analysis Helpers ──
352
+
353
+ private categorizeFailure(tc: ToolCallRecord): string {
354
+ const err = (tc.error || '').toLowerCase()
355
+ const cmd = String(tc.input?.command || tc.input?.description || '').toLowerCase()
356
+
357
+ if (err.includes('timeout') || err.includes('timed out')) return 'timeout'
358
+ if (cmd.includes('--force') || cmd.includes('rm -rf')) return 'tool-params'
359
+ if (err.includes('import') || err.includes('module') || err.includes('.js')) return 'import'
360
+ if (tc.name === 'Grep' && err.includes('no matches')) return 'search'
361
+ if (err.includes('permission') || err.includes('denied')) return 'tool-params'
362
+
363
+ return 'semantic'
364
+ }
365
+
366
+ private gatherEvidence(tc: ToolCallRecord, allCalls: ToolCallRecord[]): string[] {
367
+ const evidence: string[] = []
368
+ evidence.push(`${tc.name}: ${this.summarizeToolInput(tc)}`)
369
+
370
+ // Find similar failures in the same batch
371
+ const similar = allCalls.filter(
372
+ (c) => c !== tc && !c.success && this.categorizeFailure(c) === this.categorizeFailure(tc),
373
+ )
374
+ if (similar.length > 0) {
375
+ evidence.push(`同一轮中还有 ${similar.length} 个同类失败`)
376
+ }
377
+
378
+ return evidence
379
+ }
380
+
381
+ private suggestFix(category: string, tc: ToolCallRecord): string {
382
+ switch (category) {
383
+ case 'timeout':
384
+ return `为 ${tc.name} 增加 timeout 参数至 300000ms(5分钟)`
385
+ case 'tool-params':
386
+ return `检查 ${tc.name} 的参数安全性,考虑添加 dangerouslyDisableSandbox`
387
+ case 'import':
388
+ return '检查 ESM 模块导入是否缺少 .js 扩展名'
389
+ case 'search':
390
+ return '建议先用 Glob 缩小搜索范围,再使用 Grep'
391
+ default:
392
+ return '人工审查此工具调用的参数和上下文'
393
+ }
394
+ }
395
+
396
+ private extractDecisions(userMessage: string, assistantContent: string): string[] {
397
+ const decisions: string[] = []
398
+ const decisionPatterns = [
399
+ /决定使用\s*(.+)/g,
400
+ /选择\s*(.+?)(?:作为|方案)/g,
401
+ /采用\s*(.+?)(?:方案|架构|设计)/g,
402
+ /decided?\s*(?:to|on)\s*(.+)/gi,
403
+ /opted\s*(?:for|to)\s*(.+)/gi,
404
+ ]
405
+
406
+ for (const pattern of decisionPatterns) {
407
+ let match: RegExpExecArray | null
408
+ while ((match = pattern.exec(assistantContent)) !== null) {
409
+ decisions.push(match[1]!.trim())
410
+ }
411
+ }
412
+
413
+ // Also check for explicit decisions in user message
414
+ const userDecisionMatch = userMessage.match(/用\s*(.+?)(?:吧|方案|方法)/)
415
+ if (userDecisionMatch?.[1]) {
416
+ decisions.push(`用户决定: ${userDecisionMatch[1].trim()}`)
417
+ }
418
+
419
+ return decisions.slice(0, 5)
420
+ }
421
+
422
+ private extractActionItems(assistantContent: string, toolCalls: ToolCallRecord[]): string[] {
423
+ const items: string[] = []
424
+
425
+ // Check for TODO markers in assistant content
426
+ const todoMatches = assistantContent.matchAll(/[-*]\s*\[ \]\s*(.+)/g)
427
+ for (const match of todoMatches) {
428
+ items.push(match[1]!.trim())
429
+ }
430
+
431
+ // Failed tool calls become action items
432
+ for (const tc of toolCalls) {
433
+ if (!tc.success) {
434
+ items.push(`修复 ${tc.name} 调用失败: ${tc.error || 'unknown'}`)
435
+ }
436
+ }
437
+
438
+ return items.slice(0, 8)
439
+ }
440
+
441
+ private buildSummary(
442
+ successes: string[],
443
+ failures: string[],
444
+ insights: CrsiInsight[],
445
+ decisions: string[],
446
+ ): string {
447
+ const parts: string[] = []
448
+
449
+ if (successes.length > 0) {
450
+ parts.push(`${successes.length} 个工具调用成功`)
451
+ }
452
+ if (failures.length > 0) {
453
+ parts.push(`${failures.length} 个失败`)
454
+ }
455
+ if (insights.length > 0) {
456
+ const criticalCount = insights.filter((i) => i.severity === 'critical').length
457
+ if (criticalCount > 0) {
458
+ parts.push(`${criticalCount} 个关键 CRSI 洞察`)
459
+ } else {
460
+ parts.push(`${insights.length} 个 CRSI 洞察`)
461
+ }
462
+ }
463
+ if (decisions.length > 0) {
464
+ parts.push(`${decisions.length} 个关键决策`)
465
+ }
466
+
467
+ return parts.length > 0 ? parts.join(',') : '本轮无特殊事件'
468
+ }
469
+
470
+ private summarizeToolInput(tc: ToolCallRecord): string {
471
+ switch (tc.name) {
472
+ case 'Bash':
473
+ return String(tc.input?.command || '').slice(0, 80)
474
+ case 'Read':
475
+ return String(tc.input?.file_path || '').slice(0, 80)
476
+ case 'Write':
477
+ return String(tc.input?.file_path || '').slice(0, 80)
478
+ case 'Edit':
479
+ return `${String(tc.input?.file_path || '').slice(0, 60)}: ${String(tc.input?.old_string || '').slice(0, 20)}...`
480
+ case 'Grep':
481
+ return String(tc.input?.pattern || '').slice(0, 80)
482
+ case 'Agent':
483
+ return String(tc.input?.description || tc.input?.prompt || '').slice(0, 80)
484
+ default:
485
+ return JSON.stringify(tc.input).slice(0, 80)
486
+ }
487
+ }
488
+
489
+ // ── Private: Persistence ──
490
+
491
+ private formatReflectionContent(reflection: TurnReflection): string {
492
+ const lines: string[] = [
493
+ `# 会话复盘: ${reflection.id}`,
494
+ '',
495
+ `**会话**: ${reflection.sessionId}`,
496
+ `**时间**: ${reflection.timestamp}`,
497
+ `**摘要**: ${reflection.summary}`,
498
+ '',
499
+ '---',
500
+ '',
501
+ '## 成功项',
502
+ '',
503
+ ...(reflection.successes.length > 0
504
+ ? reflection.successes.map((s) => `- ✅ ${s}`)
505
+ : ['- _(本轮无记录的成功项)_']),
506
+ '',
507
+ '## 失败项',
508
+ '',
509
+ ...(reflection.failures.length > 0
510
+ ? reflection.failures.map((f) => `- ❌ ${f}`)
511
+ : ['- _(本轮无失败)_']),
512
+ '',
513
+ '## CRSI 洞察',
514
+ '',
515
+ ...(reflection.crsiInsights.length > 0
516
+ ? reflection.crsiInsights.map(
517
+ (i) =>
518
+ `- 🔧 [${i.severity === 'critical' ? '⚠️' : '📝'} ${i.category}] ${i.description}\n → 建议: ${i.suggestion}${i.autoApplicable ? ' _(可自动应用)_' : ''}`,
519
+ )
520
+ : ['- _(本轮无 CRSI 洞察)_']),
521
+ '',
522
+ '## 关键决策',
523
+ '',
524
+ ...(reflection.decisions.length > 0
525
+ ? reflection.decisions.map((d) => `- 🎯 ${d}`)
526
+ : ['- _(本轮无关键决策)_']),
527
+ '',
528
+ '## 待办项',
529
+ '',
530
+ ...(reflection.actionItems.length > 0
531
+ ? reflection.actionItems.map((a) => `- [ ] ${a}`)
532
+ : ['- _(本轮无待办项)_']),
533
+ '',
534
+ '---',
535
+ '',
536
+ `_由 CRSI Phase 2 AutoMemoryEngine 自动生成_`,
537
+ ]
538
+
539
+ return lines.join('\n')
540
+ }
541
+
542
+ private extractRelevance(reflection: TurnReflection): string[] {
543
+ const keywords = new Set<string>()
544
+
545
+ // Extract from CRSI insights
546
+ for (const insight of reflection.crsiInsights) {
547
+ keywords.add(insight.category)
548
+ }
549
+
550
+ // Extract from decisions
551
+ for (const d of reflection.decisions) {
552
+ const words = d.split(/\s+/).filter((w) => w.length > 2)
553
+ for (const w of words.slice(0, 3)) {
554
+ keywords.add(w.toLowerCase())
555
+ }
556
+ }
557
+
558
+ // Always include core tags
559
+ keywords.add('crsi-phase-2')
560
+ keywords.add('turn-reflection')
561
+ keywords.add(reflection.sessionId)
562
+
563
+ return Array.from(keywords).slice(0, 10)
564
+ }
565
+
566
+ private writeSessionSummary(): void {
567
+ const totalSuccesses = this.sessionReflections.reduce((s, r) => s + r.successes.length, 0)
568
+ const totalFailures = this.sessionReflections.reduce((s, r) => s + r.failures.length, 0)
569
+ const totalInsights = this.sessionReflections.reduce((s, r) => s + r.crsiInsights.length, 0)
570
+ const criticalInsights = this.sessionReflections.flatMap((r) =>
571
+ r.crsiInsights.filter((i) => i.severity === 'critical'),
572
+ )
573
+
574
+ const content = [
575
+ `# 会话总结`,
576
+ '',
577
+ `**会话 ID**: ${this.sessionReflections[0]?.sessionId || 'unknown'}`,
578
+ `**复盘数**: ${this.sessionReflections.length}`,
579
+ `**时间**: ${new Date().toISOString()}`,
580
+ '',
581
+ '---',
582
+ '',
583
+ '## 统计',
584
+ '',
585
+ `| 指标 | 数值 |`,
586
+ `|------|------|`,
587
+ `| 成功工具调用 | ${totalSuccesses} |`,
588
+ `| 失败工具调用 | ${totalFailures} |`,
589
+ `| CRSI 洞察 | ${totalInsights} |`,
590
+ `| 关键洞察 | ${criticalInsights.length} |`,
591
+ `| 成功率 | ${totalSuccesses + totalFailures > 0 ? Math.round((totalSuccesses / (totalSuccesses + totalFailures)) * 100) : 100}% |`,
592
+ '',
593
+ ...(criticalInsights.length > 0
594
+ ? [
595
+ '## ⚠️ 关键洞察',
596
+ '',
597
+ ...criticalInsights.map(
598
+ (i) => `- **${i.category}**: ${i.description}\n → ${i.suggestion}`,
599
+ ),
600
+ ]
601
+ : []),
602
+ '',
603
+ '---',
604
+ '',
605
+ `_由 CRSI Phase 2 AutoMemoryEngine 在会话结束时自动生成_`,
606
+ ].join('\n')
607
+
608
+ const name = `session-summary-${this.sessionReflections[0]?.sessionId || Date.now().toString(36)}`
609
+ this.memoryManager.write(name, content, {
610
+ type: 'feedback',
611
+ relevance: ['session-summary', 'crsi-phase-2', ...criticalInsights.map((i) => i.category)],
612
+ why: 'CRSI Phase 2 会话级自动总结',
613
+ howToApply: '在下次会话开始时回顾,重点关注关键洞察和失败模式',
614
+ })
615
+ }
616
+ }