newtype-profile 1.0.47 → 1.0.49

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/README.md CHANGED
@@ -329,18 +329,23 @@ newtype-profile includes an automatic memory system for cross-session knowledge
329
329
  ### How It Works
330
330
 
331
331
  1. **Auto-save**: When a conversation ends (session.idle), key information is extracted and saved to `.opencode/memory/YYYY-MM-DD.md`
332
- 2. **Auto-archive**: Logs older than 7 days are automatically consolidated into `.opencode/MEMORY.md`
333
- 3. **AI Awareness**: Chief knows about the memory system and can query it when needed
332
+ 2. **Full transcript**: A complete conversation log is stored in `.opencode/memory/full/<sessionID>.md` (overwrites per session)
333
+ 3. **Auto-archive**: Logs older than 7 days are consolidated into `.opencode/MEMORY.md`
334
+ 4. **Deep summaries**: If a daily entry includes Decisions/TODOs or tags (#project/#preference/#policy/#important), the archivist agent summarizes the full transcript in background
335
+ 5. **AI Awareness**: Chief knows about the memory system and can query it when needed
334
336
 
335
337
  ### File Structure
336
338
 
337
339
  ```
338
340
  your-project/
339
341
  └── .opencode/
340
- ├── MEMORY.md # Long-term memory (archived)
342
+ ├── MEMORY.md # Long-term memory (archived + deep summaries)
341
343
  └── memory/
342
- ├── 2026-01-29.md # Today's conversation log
343
- ├── 2026-01-28.md # Yesterday's log
344
+ ├── 2026-01-29.md # Daily summaries
345
+ ├── 2026-01-28.md # Daily summaries
346
+ ├── full/
347
+ │ ├── ses_xxxx.md # Full transcript per session
348
+ │ └── ...
344
349
  └── ...
345
350
  ```
346
351
 
package/README.zh-cn.md CHANGED
@@ -329,18 +329,23 @@ newtype-profile 内置了跨会话记忆系统,自动保存重要信息:
329
329
  ### 工作原理
330
330
 
331
331
  1. **自动保存**:对话结束时(session.idle),关键信息被提取并保存到 `.opencode/memory/YYYY-MM-DD.md`
332
- 2. **自动归档**:超过 7 天的日志自动合并到 `.opencode/MEMORY.md`
333
- 3. **AI 感知**:Chief 知道记忆系统的存在,需要时会主动查询
332
+ 2. **完整对话**:每个 session 的全文日志保存到 `.opencode/memory/full/<sessionID>.md`(覆盖写)
333
+ 3. **自动归档**:超过 7 天的日志自动合并到 `.opencode/MEMORY.md`
334
+ 4. **深度摘要**:当日记含 Decisions/TODOs 或标签(#project/#preference/#policy/#important)时,archivist 会后台读取全文生成深度摘要
335
+ 5. **AI 感知**:Chief 知道记忆系统的存在,需要时会主动查询
334
336
 
335
337
  ### 文件结构
336
338
 
337
339
  ```
338
340
  你的项目/
339
341
  └── .opencode/
340
- ├── MEMORY.md # 长期记忆(归档后)
342
+ ├── MEMORY.md # 长期记忆(含深度摘要)
341
343
  └── memory/
342
- ├── 2026-01-29.md # 今天的对话记录
343
- ├── 2026-01-28.md # 昨天的记录
344
+ ├── 2026-01-29.md # 每日摘要
345
+ ├── 2026-01-28.md # 每日摘要
346
+ ├── full/
347
+ │ ├── ses_xxxx.md # 每个 session 的完整记录
348
+ │ └── ...
344
349
  └── ...
345
350
  ```
346
351
 
package/dist/cli/index.js CHANGED
@@ -2253,7 +2253,7 @@ var require_picocolors = __commonJS((exports, module) => {
2253
2253
  var require_package = __commonJS((exports, module) => {
2254
2254
  module.exports = {
2255
2255
  name: "newtype-profile",
2256
- version: "1.0.47",
2256
+ version: "1.0.49",
2257
2257
  description: "AI Agent Collaboration System for Content Creation - Based on oh-my-opencode",
2258
2258
  main: "dist/index.js",
2259
2259
  types: "dist/index.d.ts",
@@ -1 +1 @@
1
- export declare const MEMORY_CONSOLIDATE_TEMPLATE = "Consolidate daily memory logs into long-term memory.\n\n## USAGE\n\n`/memory-consolidate`\n\n## WHAT TO DO\n\nRead all files in `.opencode/memory/` directory and consolidate important information into `.opencode/MEMORY.md`.\n\n### Step 1: Read Daily Logs\n\nUse `glob` to find all memory files:\n```\nglob(\".opencode/memory/*.md\")\n```\n\nThen `read` each file to understand the contents.\n\n### Step 2: Extract Key Information\n\nFrom the daily logs, identify:\n1. **User Preferences** - Recurring patterns in how user likes things done\n2. **Important Decisions** - Choices made that affect future work\n3. **Project Milestones** - Significant completions or achievements\n4. **Recurring Problems** - Issues that came up multiple times and their solutions\n5. **Key Insights** - Valuable learnings worth preserving\n\n### Step 3: Update MEMORY.md\n\nRead existing `.opencode/MEMORY.md` (create if doesn't exist).\n\nAppend new consolidated information using this structure:\n\n```markdown\n## Consolidated: YYYY-MM-DD\n\n### User Preferences\n- [preference 1]\n- [preference 2]\n\n### Decisions Made\n- [decision with context]\n\n### Lessons Learned\n- [insight or lesson]\n\n---\n```\n\n### Step 4: Archive Processed Logs\n\nAfter consolidation, you may suggest to the user whether to:\n- Keep the daily logs for reference\n- Delete processed daily logs to save space\n\n## OUTPUT FORMAT\n\nAfter consolidation:\n```\n\u2705 Memory consolidated\n\n**Processed:** X daily log files\n**Extracted:**\n- Y user preferences\n- Z decisions\n- W insights\n\n**Updated:** .opencode/MEMORY.md\n\n\uD83D\uDCA1 Tip: Daily logs in .opencode/memory/ are preserved. \n Delete them manually if you want to save space.\n```\n\nIf no memory files found:\n```\n\uD83D\uDCED No memory files to consolidate\n\nMemory files are automatically created after conversations.\nCheck back after a few chat sessions.\n```\n\n## IMPORTANT\n\n- Never delete information from MEMORY.md, only append\n- Preserve existing structure and content\n- Be selective - only consolidate truly important information\n- Use user's language (Chinese/English) based on existing content\n";
1
+ export declare const MEMORY_CONSOLIDATE_TEMPLATE = "Consolidate daily memory logs into long-term memory.\n\n## USAGE\n\n`/memory-consolidate`\n\n## WHAT TO DO\n\nRead all files in `.opencode/memory/` directory and consolidate important information into `.opencode/MEMORY.md`.\nIf a daily log entry contains Decisions, TODOs, or critical tags (#project, #preference, #policy, #important),\nalso pull the full transcript from `.opencode/memory/full/<sessionID>.md` for deeper summarization.\n\n### Step 1: Read Daily Logs\n\nUse `glob` to find all memory files:\n```\nglob(\".opencode/memory/*.md\")\n```\n\nThen `read` each file to understand the contents.\n\n### Step 2: Extract Key Information\n\nFrom the daily logs, identify:\n1. **User Preferences** - Recurring patterns in how user likes things done\n2. **Important Decisions** - Choices made that affect future work\n3. **Project Milestones** - Significant completions or achievements\n4. **Recurring Problems** - Issues that came up multiple times and their solutions\n5. **Key Insights** - Valuable learnings worth preserving\n\nIf deep-summary trigger conditions are met, read the full transcript for that session and extract additional\npreferences, decisions, and lessons.\n\n### Step 3: Update MEMORY.md\n\nRead existing `.opencode/MEMORY.md` (create if doesn't exist).\n\nAppend new consolidated information using this structure:\n\n```markdown\n## Consolidated: YYYY-MM-DD\n\n### User Preferences\n- [preference 1]\n- [preference 2]\n\n### Decisions Made\n- [decision with context]\n\n### Lessons Learned\n- [insight or lesson]\n\n### Deep Summaries (when triggered)\n- [sessionID] preference/decision/lesson from full transcript\n\n---\n```\n\n### Step 4: Archive Processed Logs\n\nAfter consolidation, you may suggest to the user whether to:\n- Keep the daily logs for reference\n- Delete processed daily logs to save space\n\n## OUTPUT FORMAT\n\nAfter consolidation:\n```\n\u2705 Memory consolidated\n\n**Processed:** X daily log files\n**Extracted:**\n- Y user preferences\n- Z decisions\n- W insights\n\n**Updated:** .opencode/MEMORY.md\n\n\uD83D\uDCA1 Tip: Daily logs in .opencode/memory/ are preserved. \n Delete them manually if you want to save space.\n```\n\nIf no memory files found:\n```\n\uD83D\uDCED No memory files to consolidate\n\nMemory files are automatically created after conversations.\nCheck back after a few chat sessions.\n```\n\n## IMPORTANT\n\n- Never delete information from MEMORY.md, only append\n- Preserve existing structure and content\n- Be selective - only consolidate truly important information\n- Use user's language (Chinese/English) based on existing content\n";
@@ -1,8 +1,8 @@
1
1
  export declare const HOOK_NAME = "memory-system";
2
- /** Minimum number of messages to trigger memory save */
3
- export declare const MIN_MESSAGES_TO_SAVE = 4;
4
2
  /** Memory storage directory relative to project root */
5
3
  export declare const MEMORY_DIR = ".opencode/memory";
4
+ /** Full transcript storage directory relative to project root */
5
+ export declare const FULL_MEMORY_DIR = ".opencode/memory/full";
6
6
  /** Long-term memory file */
7
7
  export declare const MEMORY_FILE = ".opencode/MEMORY.md";
8
8
  /** Grace period before saving (ms) - allows for quick follow-ups */
@@ -11,3 +11,5 @@ export declare const SAVE_GRACE_PERIOD_MS = 5000;
11
11
  export declare const MAX_SUMMARY_LENGTH = 2000;
12
12
  /** Days after which daily logs are auto-archived to MEMORY.md */
13
13
  export declare const ARCHIVE_AFTER_DAYS = 7;
14
+ /** Tags that trigger deep summary from full transcripts */
15
+ export declare const DEEP_SUMMARY_TAGS: readonly ["#project", "#preference", "#policy", "#important"];
@@ -1,10 +1,34 @@
1
- import type { MemoryEntry } from "./types";
1
+ import type { MemoryEntry, MemoryEntryMessage } from "./types";
2
2
  export interface ArchiveResult {
3
3
  archived: string[];
4
4
  totalFiles: number;
5
5
  needsArchive: boolean;
6
6
  }
7
+ export interface FullTranscriptBlock {
8
+ role: string;
9
+ content: string;
10
+ }
11
+ export interface MemorySummary {
12
+ userPreferences: string[];
13
+ decisions: string[];
14
+ lessons: string[];
15
+ }
16
+ export interface DailyLogSession {
17
+ sessionID?: string;
18
+ raw: string;
19
+ tags: string[];
20
+ decisions: string[];
21
+ todos: string[];
22
+ }
7
23
  export declare function checkArchiveNeeded(projectDir: string): ArchiveResult;
8
- export declare function archiveOldMemories(projectDir: string): ArchiveResult;
24
+ export declare function archiveOldMemories(projectDir: string, options?: {
25
+ deepSummarizer?: (session: DailyLogSession, fullContent: string) => Promise<string | null>;
26
+ }): Promise<ArchiveResult>;
27
+ export declare function parseDailyLogSessions(content: string): DailyLogSession[];
28
+ export declare function shouldDeepSummarize(session: DailyLogSession): boolean;
29
+ export declare function getFullTranscriptPath(projectDir: string, sessionID: string): string;
30
+ export declare function parseFullTranscript(content: string): FullTranscriptBlock[];
31
+ export declare function summarizeFullTranscript(content: string): MemorySummary;
9
32
  export declare function appendMemoryEntry(projectDir: string, entry: MemoryEntry): boolean;
10
33
  export declare function hasMemoryForSession(projectDir: string, sessionID: string): boolean;
34
+ export declare function saveFullTranscript(projectDir: string, sessionID: string, messages: MemoryEntryMessage[]): boolean;
@@ -5,6 +5,12 @@ export interface MemoryEntry {
5
5
  keyPoints: string[];
6
6
  decisions?: string[];
7
7
  todos?: string[];
8
+ tags?: string[];
9
+ }
10
+ export interface MemoryEntryMessage {
11
+ role: string;
12
+ text: string;
13
+ timestamp?: string;
8
14
  }
9
15
  export interface SessionState {
10
16
  saved: boolean;
package/dist/index.js CHANGED
@@ -24326,15 +24326,24 @@ ${buildStandaloneVerificationReminder(subagentSessionId)}
24326
24326
  }
24327
24327
  // src/hooks/memory-system/constants.ts
24328
24328
  var HOOK_NAME6 = "memory-system";
24329
- var MIN_MESSAGES_TO_SAVE = 4;
24330
24329
  var MEMORY_DIR = ".opencode/memory";
24330
+ var FULL_MEMORY_DIR = ".opencode/memory/full";
24331
24331
  var MEMORY_FILE = ".opencode/MEMORY.md";
24332
24332
  var SAVE_GRACE_PERIOD_MS = 5000;
24333
24333
  var MAX_SUMMARY_LENGTH = 2000;
24334
24334
  var ARCHIVE_AFTER_DAYS = 7;
24335
+ var DEEP_SUMMARY_TAGS = ["#project", "#preference", "#policy", "#important"];
24335
24336
 
24336
24337
  // src/hooks/memory-system/storage.ts
24337
- import { existsSync as existsSync40, mkdirSync as mkdirSync12, appendFileSync as appendFileSync6, readFileSync as readFileSync26, readdirSync as readdirSync15, unlinkSync as unlinkSync10 } from "fs";
24338
+ import {
24339
+ existsSync as existsSync40,
24340
+ mkdirSync as mkdirSync12,
24341
+ appendFileSync as appendFileSync6,
24342
+ readFileSync as readFileSync26,
24343
+ readdirSync as readdirSync15,
24344
+ unlinkSync as unlinkSync10,
24345
+ writeFileSync as writeFileSync16
24346
+ } from "fs";
24338
24347
  import { join as join48 } from "path";
24339
24348
  function ensureMemoryDir(projectDir) {
24340
24349
  const memoryPath = join48(projectDir, MEMORY_DIR);
@@ -24343,6 +24352,13 @@ function ensureMemoryDir(projectDir) {
24343
24352
  }
24344
24353
  return memoryPath;
24345
24354
  }
24355
+ function ensureFullMemoryDir(projectDir) {
24356
+ const memoryPath = join48(projectDir, FULL_MEMORY_DIR);
24357
+ if (!existsSync40(memoryPath)) {
24358
+ mkdirSync12(memoryPath, { recursive: true });
24359
+ }
24360
+ return memoryPath;
24361
+ }
24346
24362
  function getDateFileName() {
24347
24363
  const now = new Date;
24348
24364
  return `${now.toISOString().split("T")[0]}.md`;
@@ -24367,6 +24383,14 @@ function getDaysDiff(date) {
24367
24383
  date.setHours(0, 0, 0, 0);
24368
24384
  return Math.floor((now.getTime() - date.getTime()) / (1000 * 60 * 60 * 24));
24369
24385
  }
24386
+ function extractSectionItems(block, title) {
24387
+ const pattern = new RegExp(`\\*\\*${title}:\\*\\*\\n([\\s\\S]*?)(?:\\n\\*\\*|$)`);
24388
+ const match = block.match(pattern);
24389
+ if (!match)
24390
+ return [];
24391
+ return match[1].split(`
24392
+ `).map((line) => line.trim()).filter((line) => line.startsWith("- ")).map((line) => line.replace(/^\-\s+/, "")).filter(Boolean);
24393
+ }
24370
24394
  function checkArchiveNeeded(projectDir) {
24371
24395
  const memoryDir = join48(projectDir, MEMORY_DIR);
24372
24396
  if (!existsSync40(memoryDir)) {
@@ -24386,7 +24410,7 @@ function checkArchiveNeeded(projectDir) {
24386
24410
  needsArchive: oldFiles.length > 0
24387
24411
  };
24388
24412
  }
24389
- function archiveOldMemories(projectDir) {
24413
+ async function archiveOldMemories(projectDir, options) {
24390
24414
  const checkResult = checkArchiveNeeded(projectDir);
24391
24415
  if (!checkResult.needsArchive) {
24392
24416
  return checkResult;
@@ -24403,13 +24427,65 @@ function archiveOldMemories(projectDir) {
24403
24427
  try {
24404
24428
  const filePath = join48(memoryDir, file);
24405
24429
  const content = readFileSync26(filePath, "utf-8");
24430
+ const sessions = parseDailyLogSessions(content);
24406
24431
  const dateMatch = file.match(/^(\d{4}-\d{2}-\d{2})/);
24407
24432
  const dateStr = dateMatch ? dateMatch[1] : file;
24408
24433
  archivedContent.push(`### From ${dateStr}
24409
24434
 
24410
24435
  `);
24411
- const contentWithoutHeader = content.replace(/^# Memory Log.*\n\n?/, "");
24412
- archivedContent.push(contentWithoutHeader);
24436
+ const summaryBlocks = [];
24437
+ const deepBlocks = [];
24438
+ for (const session of sessions) {
24439
+ summaryBlocks.push(session.raw, "", "---", "");
24440
+ if (session.sessionID && shouldDeepSummarize(session)) {
24441
+ const fullPath = getFullTranscriptPath(projectDir, session.sessionID);
24442
+ if (existsSync40(fullPath)) {
24443
+ const fullContent = readFileSync26(fullPath, "utf-8");
24444
+ let deepSummary = null;
24445
+ if (options?.deepSummarizer) {
24446
+ deepSummary = await options.deepSummarizer(session, fullContent);
24447
+ }
24448
+ if (!deepSummary) {
24449
+ const summary = summarizeFullTranscript(fullContent);
24450
+ if (summary.userPreferences.length > 0 || summary.decisions.length > 0 || summary.lessons.length > 0) {
24451
+ deepSummary = [
24452
+ summary.userPreferences.length > 0 ? [
24453
+ "**User Preferences:**",
24454
+ ...summary.userPreferences.map((item) => `- ${item}`),
24455
+ ""
24456
+ ].join(`
24457
+ `) : "",
24458
+ summary.decisions.length > 0 ? [
24459
+ "**Decisions Made:**",
24460
+ ...summary.decisions.map((item) => `- ${item}`),
24461
+ ""
24462
+ ].join(`
24463
+ `) : "",
24464
+ summary.lessons.length > 0 ? [
24465
+ "**Lessons Learned:**",
24466
+ ...summary.lessons.map((item) => `- ${item}`),
24467
+ ""
24468
+ ].join(`
24469
+ `) : ""
24470
+ ].filter(Boolean).join(`
24471
+ `);
24472
+ }
24473
+ }
24474
+ if (deepSummary) {
24475
+ deepBlocks.push(`#### Deep Summary (${session.sessionID.slice(0, 12)})`, "", deepSummary.trim(), "", "---", "");
24476
+ }
24477
+ }
24478
+ }
24479
+ }
24480
+ archivedContent.push(summaryBlocks.join(`
24481
+ `));
24482
+ if (deepBlocks.length > 0) {
24483
+ archivedContent.push(`#### Deep Summaries
24484
+
24485
+ `);
24486
+ archivedContent.push(deepBlocks.join(`
24487
+ `));
24488
+ }
24413
24489
  unlinkSync10(filePath);
24414
24490
  } catch {
24415
24491
  continue;
@@ -24430,12 +24506,108 @@ This file contains archived conversation memories.
24430
24506
  needsArchive: false
24431
24507
  };
24432
24508
  }
24509
+ function parseDailyLogSessions(content) {
24510
+ const cleaned = content.replace(/^# Memory Log.*\n\n?/, "");
24511
+ const blocks = cleaned.split(/\n---\n/).map((block) => block.trim()).filter(Boolean);
24512
+ return blocks.map((block) => {
24513
+ const sessionMatch = block.match(/SessionID:\s*(.+)/);
24514
+ const tags = extractSectionItems(block, "Tags");
24515
+ const decisions = extractSectionItems(block, "Decisions");
24516
+ const todos = extractSectionItems(block, "TODOs").map((item) => item.replace(/^\[ \]\s+/, ""));
24517
+ return {
24518
+ sessionID: sessionMatch?.[1]?.trim(),
24519
+ raw: block,
24520
+ tags,
24521
+ decisions,
24522
+ todos
24523
+ };
24524
+ });
24525
+ }
24526
+ function shouldDeepSummarize(session) {
24527
+ if (session.decisions.length > 0 || session.todos.length > 0)
24528
+ return true;
24529
+ return session.tags.some((tag) => DEEP_SUMMARY_TAGS.includes(tag.toLowerCase()));
24530
+ }
24531
+ function getFullTranscriptPath(projectDir, sessionID) {
24532
+ const safeSessionID = sessionID.replace(/[^a-zA-Z0-9_-]/g, "_");
24533
+ return join48(projectDir, FULL_MEMORY_DIR, `${safeSessionID}.md`);
24534
+ }
24535
+ function parseFullTranscript(content) {
24536
+ const cleaned = content.replace(/^# Full Transcript.*\n.*\n\n?/, "");
24537
+ const blocks = cleaned.split(/\n---\n/).map((block) => block.trim()).filter(Boolean);
24538
+ return blocks.map((block) => {
24539
+ const lines = block.split(`
24540
+ `);
24541
+ const title = lines.shift() || "";
24542
+ const roleMatch = title.match(/^##\s+([A-Z]+)\b/);
24543
+ return {
24544
+ role: roleMatch?.[1]?.toLowerCase() || "unknown",
24545
+ content: lines.join(`
24546
+ `).trim()
24547
+ };
24548
+ });
24549
+ }
24550
+ function summarizeFullTranscript(content) {
24551
+ const blocks = parseFullTranscript(content);
24552
+ const userPreferences = [];
24553
+ const decisions = [];
24554
+ const lessons = [];
24555
+ for (const block of blocks) {
24556
+ const text = block.content;
24557
+ const preferencePatterns = [
24558
+ /\u504F\u597D[\uFF1A:](.+)/g,
24559
+ /\u559C\u6B22[\uFF1A:](.+)/g,
24560
+ /prefer(?:s|red)?[\uFF1A:](.+)/gi,
24561
+ /preference(?:s)?[\uFF1A:](.+)/gi
24562
+ ];
24563
+ for (const pattern of preferencePatterns) {
24564
+ for (const match of text.matchAll(pattern)) {
24565
+ if (match[1])
24566
+ userPreferences.push(match[1].trim());
24567
+ }
24568
+ }
24569
+ const decisionPatterns = [
24570
+ /\u51B3\u5B9A[\uFF1A:](.+)/g,
24571
+ /\u51B3\u7B56[\uFF1A:](.+)/g,
24572
+ /decided?[\uFF1A:](.+)/gi,
24573
+ /chosen?[\uFF1A:](.+)/gi,
24574
+ /going with\s+(.+)/gi,
24575
+ /will use\s+(.+)/gi
24576
+ ];
24577
+ for (const pattern of decisionPatterns) {
24578
+ for (const match of text.matchAll(pattern)) {
24579
+ if (match[1])
24580
+ decisions.push(match[1].trim());
24581
+ }
24582
+ }
24583
+ const lessonPatterns = [
24584
+ /\u7ED3\u8BBA[\uFF1A:](.+)/g,
24585
+ /\u7ECF\u9A8C[\uFF1A:](.+)/g,
24586
+ /\u6559\u8BAD[\uFF1A:](.+)/g,
24587
+ /lesson(?:s)?[\uFF1A:](.+)/gi,
24588
+ /insight(?:s)?[\uFF1A:](.+)/gi
24589
+ ];
24590
+ for (const pattern of lessonPatterns) {
24591
+ for (const match of text.matchAll(pattern)) {
24592
+ if (match[1])
24593
+ lessons.push(match[1].trim());
24594
+ }
24595
+ }
24596
+ }
24597
+ const unique = (items) => Array.from(new Set(items)).slice(0, 10);
24598
+ return {
24599
+ userPreferences: unique(userPreferences),
24600
+ decisions: unique(decisions),
24601
+ lessons: unique(lessons)
24602
+ };
24603
+ }
24433
24604
  function appendMemoryEntry(projectDir, entry) {
24434
24605
  try {
24435
24606
  const memoryDir = ensureMemoryDir(projectDir);
24436
24607
  const filePath = join48(memoryDir, getDateFileName());
24437
24608
  const sections = [
24438
24609
  `## Session: ${entry.sessionID.slice(0, 12)} (${formatTime(new Date(entry.timestamp))})`,
24610
+ `SessionID: ${entry.sessionID}`,
24439
24611
  ""
24440
24612
  ];
24441
24613
  if (entry.summary) {
@@ -24457,6 +24629,11 @@ function appendMemoryEntry(projectDir, entry) {
24457
24629
  entry.todos.forEach((todo) => sections.push(`- [ ] ${todo}`));
24458
24630
  sections.push("");
24459
24631
  }
24632
+ if (entry.tags && entry.tags.length > 0) {
24633
+ sections.push("**Tags:**");
24634
+ entry.tags.forEach((tag) => sections.push(`- ${tag}`));
24635
+ sections.push("");
24636
+ }
24460
24637
  sections.push("---", "");
24461
24638
  const content = sections.join(`
24462
24639
  `);
@@ -24480,7 +24657,28 @@ function hasMemoryForSession(projectDir, sessionID) {
24480
24657
  if (!existsSync40(filePath))
24481
24658
  return false;
24482
24659
  const content = readFileSync26(filePath, "utf-8");
24483
- return content.includes(`Session: ${sessionID.slice(0, 12)}`);
24660
+ return content.includes(`SessionID: ${sessionID}`) || content.includes(`Session: ${sessionID.slice(0, 12)}`);
24661
+ } catch {
24662
+ return false;
24663
+ }
24664
+ }
24665
+ function saveFullTranscript(projectDir, sessionID, messages) {
24666
+ try {
24667
+ const memoryDir = ensureFullMemoryDir(projectDir);
24668
+ const safeSessionID = sessionID.replace(/[^a-zA-Z0-9_-]/g, "_");
24669
+ const filePath = join48(memoryDir, `${safeSessionID}.md`);
24670
+ const sections = [
24671
+ `# Full Transcript - ${sessionID}`,
24672
+ `Generated: ${new Date().toISOString()}`,
24673
+ ""
24674
+ ];
24675
+ for (const message of messages) {
24676
+ const timestamp2 = message.timestamp ? ` (${message.timestamp})` : "";
24677
+ sections.push(`## ${message.role.toUpperCase()}${timestamp2}`, "", message.text || "", "", "---", "");
24678
+ }
24679
+ writeFileSync16(filePath, sections.join(`
24680
+ `));
24681
+ return true;
24484
24682
  } catch {
24485
24683
  return false;
24486
24684
  }
@@ -24499,10 +24697,17 @@ function truncateText(text, maxLength) {
24499
24697
  function extractSessionSummary(sessionID, messages) {
24500
24698
  const userMessages = [];
24501
24699
  const assistantMessages = [];
24700
+ const tags = [];
24502
24701
  for (const msg of messages) {
24503
24702
  const text = extractTextFromParts(msg.parts);
24504
24703
  if (!text.trim())
24505
24704
  continue;
24705
+ const tagMatches = text.matchAll(/#([a-zA-Z][\w-]{1,30})/g);
24706
+ for (const match of tagMatches) {
24707
+ const tag = match[1]?.toLowerCase();
24708
+ if (tag)
24709
+ tags.push(`#${tag}`);
24710
+ }
24506
24711
  if (msg.info.role === "user") {
24507
24712
  userMessages.push(truncateText(text, 500));
24508
24713
  } else if (msg.info.role === "assistant") {
@@ -24559,15 +24764,60 @@ function extractSessionSummary(sessionID, messages) {
24559
24764
  summary,
24560
24765
  keyPoints: [...new Set(keyPoints)].slice(0, 5),
24561
24766
  decisions: [...new Set(decisions)].slice(0, 3),
24562
- todos: [...new Set(todos)].slice(0, 3)
24767
+ todos: [...new Set(todos)].slice(0, 3),
24768
+ tags: [...new Set(tags)].slice(0, 5)
24563
24769
  };
24564
24770
  }
24565
24771
 
24566
24772
  // src/hooks/memory-system/index.ts
24773
+ function extractMessageText(parts) {
24774
+ return parts.filter((p) => p.type === "text" && p.text).map((p) => p.text).join(`
24775
+ `);
24776
+ }
24777
+ function extractFullTranscript(messages) {
24778
+ return messages.map((message) => {
24779
+ const text = extractMessageText(message.parts);
24780
+ return {
24781
+ role: message.info.role,
24782
+ text
24783
+ };
24784
+ }).filter((message) => message.text.trim().length > 0);
24785
+ }
24567
24786
  var ARCHIVE_CHECK_COOLDOWN_MS = 60 * 60 * 1000;
24568
24787
  function createMemorySystemHook(ctx) {
24569
24788
  const sessionStates = new Map;
24570
24789
  const archiveState = { inProgress: false, lastCheck: 0 };
24790
+ async function runArchivistSummary(prompt) {
24791
+ try {
24792
+ const createResult = await ctx.client.session.create({
24793
+ body: {
24794
+ title: "Memory: Deep Summary"
24795
+ }
24796
+ });
24797
+ if (createResult.error)
24798
+ return null;
24799
+ const sessionID = createResult.data.id;
24800
+ subagentSessions.add(sessionID);
24801
+ await ctx.client.session.prompt({
24802
+ path: { id: sessionID },
24803
+ body: {
24804
+ agent: "archivist",
24805
+ parts: [{ type: "text", text: prompt }]
24806
+ }
24807
+ });
24808
+ const messagesResult = await ctx.client.session.messages({
24809
+ path: { id: sessionID }
24810
+ });
24811
+ const messages = messagesResult.data ?? messagesResult;
24812
+ const assistantMessages = messages.filter((m) => m.info.role === "assistant");
24813
+ const last = assistantMessages[assistantMessages.length - 1];
24814
+ const text = last?.parts.filter((p) => p.type === "text" && p.text).map((p) => p.text).join(`
24815
+ `).trim();
24816
+ return text || null;
24817
+ } catch {
24818
+ return null;
24819
+ }
24820
+ }
24571
24821
  function getOrCreateState(sessionID) {
24572
24822
  let state2 = sessionStates.get(sessionID);
24573
24823
  if (!state2) {
@@ -24611,7 +24861,34 @@ function createMemorySystemHook(ctx) {
24611
24861
  count: checkResult.archived.length
24612
24862
  });
24613
24863
  try {
24614
- const result = archiveOldMemories(ctx.directory);
24864
+ const result = await archiveOldMemories(ctx.directory, {
24865
+ deepSummarizer: async (session, fullContent) => {
24866
+ const prompt = `Summarize the full transcript into long-term memory entries.
24867
+
24868
+ Rules:
24869
+ - Output Markdown only.
24870
+ - Use sections only when relevant.
24871
+ - Keep each bullet concise (<120 chars).
24872
+ - Do NOT include sensitive data or raw conversation.
24873
+ - If nothing important, return "".
24874
+
24875
+ Required sections (only if non-empty):
24876
+ **User Preferences:**
24877
+ - ...
24878
+ **Decisions Made:**
24879
+ - ...
24880
+ **Lessons Learned:**
24881
+ - ...
24882
+
24883
+ Transcript:
24884
+ ${fullContent}`;
24885
+ try {
24886
+ return await runArchivistSummary(prompt);
24887
+ } catch {
24888
+ return null;
24889
+ }
24890
+ }
24891
+ });
24615
24892
  await ctx.client.tui.showToast({
24616
24893
  body: {
24617
24894
  title: "Memory Archived",
@@ -24647,17 +24924,11 @@ function createMemorySystemHook(ctx) {
24647
24924
  query: { directory: ctx.directory }
24648
24925
  });
24649
24926
  const messages = resp.data ?? resp;
24650
- if (messages.length < MIN_MESSAGES_TO_SAVE) {
24651
- log(`[${HOOK_NAME6}] Too few messages`, {
24652
- sessionID,
24653
- count: messages.length,
24654
- threshold: MIN_MESSAGES_TO_SAVE
24655
- });
24656
- return;
24657
- }
24658
24927
  const entry = extractSessionSummary(sessionID, messages);
24928
+ const fullTranscript = extractFullTranscript(messages);
24659
24929
  const success = appendMemoryEntry(ctx.directory, entry);
24660
- if (success) {
24930
+ const fullSuccess = saveFullTranscript(ctx.directory, sessionID, fullTranscript);
24931
+ if (success && fullSuccess) {
24661
24932
  state2.saved = true;
24662
24933
  log(`[${HOOK_NAME6}] Memory saved`, {
24663
24934
  sessionID,
@@ -24666,7 +24937,11 @@ function createMemorySystemHook(ctx) {
24666
24937
  });
24667
24938
  await checkAndArchive();
24668
24939
  } else {
24669
- log(`[${HOOK_NAME6}] Failed to save memory`, { sessionID });
24940
+ log(`[${HOOK_NAME6}] Failed to save memory`, {
24941
+ sessionID,
24942
+ summarySaved: success,
24943
+ fullSaved: fullSuccess
24944
+ });
24670
24945
  }
24671
24946
  } catch (err) {
24672
24947
  log(`[${HOOK_NAME6}] Error saving memory`, { sessionID, error: String(err) });
@@ -30031,7 +30306,7 @@ ${msg}`);
30031
30306
  // src/tools/lsp/utils.ts
30032
30307
  import { extname as extname2, resolve as resolve7 } from "path";
30033
30308
  import { fileURLToPath as fileURLToPath3 } from "url";
30034
- import { existsSync as existsSync43, readFileSync as readFileSync30, writeFileSync as writeFileSync16 } from "fs";
30309
+ import { existsSync as existsSync43, readFileSync as readFileSync30, writeFileSync as writeFileSync17 } from "fs";
30035
30310
  function findWorkspaceRoot(filePath) {
30036
30311
  let dir = resolve7(filePath);
30037
30312
  if (!existsSync43(dir) || !__require("fs").statSync(dir).isDirectory()) {
@@ -30261,7 +30536,7 @@ function applyTextEditsToFile(filePath, edits) {
30261
30536
  `));
30262
30537
  }
30263
30538
  }
30264
- writeFileSync16(filePath, lines.join(`
30539
+ writeFileSync17(filePath, lines.join(`
30265
30540
  `), "utf-8");
30266
30541
  return { success: true, editCount: edits.length };
30267
30542
  } catch (err) {
@@ -30292,7 +30567,7 @@ function applyWorkspaceEdit(edit) {
30292
30567
  if (change.kind === "create") {
30293
30568
  try {
30294
30569
  const filePath = uriToPath(change.uri);
30295
- writeFileSync16(filePath, "", "utf-8");
30570
+ writeFileSync17(filePath, "", "utf-8");
30296
30571
  result.filesModified.push(filePath);
30297
30572
  } catch (err) {
30298
30573
  result.success = false;
@@ -30303,7 +30578,7 @@ function applyWorkspaceEdit(edit) {
30303
30578
  const oldPath = uriToPath(change.oldUri);
30304
30579
  const newPath = uriToPath(change.newUri);
30305
30580
  const content = readFileSync30(oldPath, "utf-8");
30306
- writeFileSync16(newPath, content, "utf-8");
30581
+ writeFileSync17(newPath, content, "utf-8");
30307
30582
  __require("fs").unlinkSync(oldPath);
30308
30583
  result.filesModified.push(newPath);
30309
30584
  } catch (err) {
@@ -50700,8 +50975,10 @@ When analyzing problems:
50700
50975
  \u4F60\u6709\u4E00\u4E2A\u6587\u4EF6\u7CFB\u7EDF\u8BB0\u5FC6\uFF0C\u7528\u4E8E\u8DE8\u4F1A\u8BDD\u4FDD\u7559\u91CD\u8981\u4FE1\u606F\uFF1A
50701
50976
 
50702
50977
  **\u5B58\u50A8\u4F4D\u7F6E\uFF1A**
50978
+ - \`KNOWLEDGE.md\` \u2014 \u9879\u76EE\u77E5\u8BC6\u5E93\uFF08\u4ED3\u5E93\u7EA7\u3001\u7ED3\u6784\u5316\u4E8B\u5B9E\uFF09
50703
50979
  - \`.opencode/MEMORY.md\` \u2014 \u957F\u671F\u77E5\u8BC6\u6C89\u6DC0\uFF08\u6574\u7406\u540E\u7684\u7CBE\u534E\uFF09
50704
50980
  - \`.opencode/memory/\` \u2014 \u6309\u65E5\u671F\u5B58\u50A8\u7684\u5BF9\u8BDD\u8BB0\u5FC6
50981
+ - \`.opencode/memory/full/\` \u2014 \u6BCF\u4E2A session \u7684\u5B8C\u6574\u5BF9\u8BDD
50705
50982
 
50706
50983
  **\u4F55\u65F6\u67E5\u9605\u8BB0\u5FC6\uFF1A**
50707
50984
  - \u7528\u6237\u63D0\u5230"\u4E4B\u524D\u8BA8\u8BBA\u8FC7"\u3001"\u4E0A\u6B21"\u3001"\u6211\u4EEC\u51B3\u5B9A\u7684"
@@ -50709,9 +50986,10 @@ When analyzing problems:
50709
50986
  - \u7528\u6237\u95EE"\u4F60\u8FD8\u8BB0\u5F97...\u5417"
50710
50987
 
50711
50988
  **\u5982\u4F55\u4F7F\u7528\uFF1A**
50712
- 1. \`read(".opencode/MEMORY.md")\` \u2014 \u67E5\u770B\u957F\u671F\u8BB0\u5FC6
50713
- 2. \`glob(".opencode/memory/*.md")\` \u2014 \u5217\u51FA\u6240\u6709\u8BB0\u5FC6\u6587\u4EF6
50714
- 3. \`grep("\u5173\u952E\u8BCD", ".opencode/memory/")\` \u2014 \u641C\u7D22\u7279\u5B9A\u8BDD\u9898
50989
+ 1. \`read("KNOWLEDGE.md")\` \u2014 \u67E5\u770B\u9879\u76EE\u77E5\u8BC6\u5E93
50990
+ 2. \`read(".opencode/MEMORY.md")\` \u2014 \u67E5\u770B\u957F\u671F\u8BB0\u5FC6
50991
+ 3. \`glob(".opencode/memory/*.md")\` \u2014 \u5217\u51FA\u6240\u6709\u8BB0\u5FC6\u6587\u4EF6
50992
+ 4. \`grep("\u5173\u952E\u8BCD", ".opencode/memory/")\` \u2014 \u641C\u7D22\u7279\u5B9A\u8BDD\u9898
50715
50993
 
50716
50994
  **\u8BB0\u5FC6\u662F\u4F60\u7684\u8D44\u4EA7**\uFF1A\u5584\u7528\u5B83\u6765\u4FDD\u6301\u8FDE\u8D2F\u6027\uFF0C\u907F\u514D\u91CD\u590D\u8BA8\u8BBA\u5DF2\u51B3\u5B9A\u7684\u4E8B\u9879\u3002
50717
50995
  </Memory_System>
@@ -52221,6 +52499,8 @@ var MEMORY_CONSOLIDATE_TEMPLATE = `Consolidate daily memory logs into long-term
52221
52499
  ## WHAT TO DO
52222
52500
 
52223
52501
  Read all files in \`.opencode/memory/\` directory and consolidate important information into \`.opencode/MEMORY.md\`.
52502
+ If a daily log entry contains Decisions, TODOs, or critical tags (#project, #preference, #policy, #important),
52503
+ also pull the full transcript from \`.opencode/memory/full/<sessionID>.md\` for deeper summarization.
52224
52504
 
52225
52505
  ### Step 1: Read Daily Logs
52226
52506
 
@@ -52240,6 +52520,9 @@ From the daily logs, identify:
52240
52520
  4. **Recurring Problems** - Issues that came up multiple times and their solutions
52241
52521
  5. **Key Insights** - Valuable learnings worth preserving
52242
52522
 
52523
+ If deep-summary trigger conditions are met, read the full transcript for that session and extract additional
52524
+ preferences, decisions, and lessons.
52525
+
52243
52526
  ### Step 3: Update MEMORY.md
52244
52527
 
52245
52528
  Read existing \`.opencode/MEMORY.md\` (create if doesn't exist).
@@ -52259,6 +52542,9 @@ Append new consolidated information using this structure:
52259
52542
  ### Lessons Learned
52260
52543
  - [insight or lesson]
52261
52544
 
52545
+ ### Deep Summaries (when triggered)
52546
+ - [sessionID] preference/decision/lesson from full transcript
52547
+
52262
52548
  ---
52263
52549
  \`\`\`
52264
52550
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "newtype-profile",
3
- "version": "1.0.47",
3
+ "version": "1.0.49",
4
4
  "description": "AI Agent Collaboration System for Content Creation - Based on oh-my-opencode",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",