@pencil-agent/nano-pencil 1.11.24 → 1.11.25

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.
@@ -13,6 +13,7 @@ export declare class NanoMemEngine {
13
13
  private embeddingFn?;
14
14
  private static readonly AUTO_V2_LINK_PREFIX;
15
15
  private static readonly AUTO_REVIVE_MAX_ITEMS;
16
+ private static readonly CONVERSATION_PREFERENCE_PATTERNS;
16
17
  private knowledgePath;
17
18
  private lessonsPath;
18
19
  private eventsPath;
@@ -315,5 +316,9 @@ export declare class NanoMemEngine {
315
316
  private reinforceWork;
316
317
  private reconsolidateIfNeeded;
317
318
  private buildProgressiveInjectionText;
319
+ private isConversationPreference;
320
+ private selectConversationPreferences;
321
+ private rankConversationPreference;
322
+ private mergeUniqueEntries;
318
323
  }
319
324
  //# sourceMappingURL=engine.d.ts.map
@@ -31,6 +31,23 @@ export class NanoMemEngine {
31
31
  embeddingFn;
32
32
  static AUTO_V2_LINK_PREFIX = "auto:v2:";
33
33
  static AUTO_REVIVE_MAX_ITEMS = 2;
34
+ static CONVERSATION_PREFERENCE_PATTERNS = [
35
+ /\bcall me\b/i,
36
+ /\baddress me\b/i,
37
+ /\bmy name is\b/i,
38
+ /\bi am called\b/i,
39
+ /\bspeak (?:to me )?(?:like|in)\b/i,
40
+ /\btalk (?:to me )?(?:like|in)\b/i,
41
+ /\buse (?:a |the )?(?:tone|style|voice)\b/i,
42
+ /\b(?:tone|style|voice|persona)\b/i,
43
+ /叫我/,
44
+ /称呼我/,
45
+ /称呼用户/,
46
+ /语气/,
47
+ /口吻/,
48
+ /风格/,
49
+ /说话方式/,
50
+ ];
34
51
  knowledgePath;
35
52
  lessonsPath;
36
53
  eventsPath;
@@ -380,6 +397,7 @@ export class NanoMemEngine {
380
397
  const tieredEvents = tierEntries(events, project, contextTags, hl, sw, pr);
381
398
  const tieredPrefs = tierEntries(prefs, project, contextTags, hl, sw, pr);
382
399
  const tieredFacets = tierEntries(facets, project, contextTags, hl, sw, pr);
400
+ const forcedConversationPrefs = this.selectConversationPreferences(prefs);
383
401
  // Budget calculation
384
402
  const totalChars = this.cfg.tokenBudget * 4;
385
403
  const activeChars = Math.floor(totalChars * pr.budgetActive);
@@ -406,7 +424,7 @@ export class NanoMemEngine {
406
424
  const activeKnowledge = pickTop(tieredKnowledge.active, scoreFn, activeLen, activeBudgetPer);
407
425
  const activeLessons = pickTop(tieredLessons.active, scoreFn, activeLen, activeBudgetPer);
408
426
  const activeEvents = pickTop(tieredEvents.active, scoreFn, activeLen, activeBudgetPer);
409
- const activePrefs = pickTop(tieredPrefs.active, scoreFn, activeLen, activeBudgetPer);
427
+ const activePrefs = this.mergeUniqueEntries(forcedConversationPrefs, pickTop(tieredPrefs.active, scoreFn, activeLen, activeBudgetPer));
410
428
  const activeFacets = pickTop(tieredFacets.active, scoreFn, activeLen, activeBudgetPer);
411
429
  const activeProcedural = pickTop(procedural.filter((item) => semanticProcedureIds.has(item.id) || proceduralScoreFn(item) >= 0.45), proceduralScoreFn, proceduralLen, Math.max(400, Math.floor(proceduralChars * 0.55)));
412
430
  const activeEpisodeMemories = pickTop(v2Episodes.filter((item) => semanticEpisodeIds.has(item.id) || episodeScoreFn(item) >= 0.45), episodeScoreFn, episodeMemoryLen, Math.max(320, Math.floor(activeChars * 0.18)));
@@ -421,7 +439,8 @@ export class NanoMemEngine {
421
439
  const cueKnowledge = pickTop(tieredKnowledge.cue, scoreFn, cueLen, cueBudgetPer);
422
440
  const cueLessons = pickTop(tieredLessons.cue, scoreFn, cueLen, cueBudgetPer);
423
441
  const cueEvents = pickTop(tieredEvents.cue, scoreFn, cueLen, cueBudgetPer);
424
- const cuePrefs = pickTop(tieredPrefs.cue, scoreFn, cueLen, cueBudgetPer);
442
+ const forcedConversationPrefIds = new Set(forcedConversationPrefs.map((entry) => entry.id));
443
+ const cuePrefs = pickTop(tieredPrefs.cue.filter((entry) => !forcedConversationPrefIds.has(entry.id)), scoreFn, cueLen, cueBudgetPer);
425
444
  const cueFacets = pickTop(tieredFacets.cue, scoreFn, cueLen, cueBudgetPer);
426
445
  const graphContextIds = new Set(graphContext.map((neighbor) => neighbor.entry.id));
427
446
  const dedupeCue = (entries) => entries.filter((entry) => !graphContextIds.has(entry.id));
@@ -2396,6 +2415,7 @@ export class NanoMemEngine {
2396
2415
  const semanticSectionTitle = "Semantic Abstractions";
2397
2416
  // ── Active tier: full detail ──
2398
2417
  const activeLines = [];
2418
+ const conversationPreferenceLines = [];
2399
2419
  const keyEventLines = [];
2400
2420
  const stateLines = [];
2401
2421
  const formatActiveEntry = (e) => {
@@ -2419,6 +2439,10 @@ export class NanoMemEngine {
2419
2439
  return formatActiveEntry(e);
2420
2440
  };
2421
2441
  const pushActiveEntry = (entry) => {
2442
+ if (this.isConversationPreference(entry)) {
2443
+ conversationPreferenceLines.push(formatActiveEntry(entry));
2444
+ return;
2445
+ }
2422
2446
  if (entry.stability === "situational" || entry.stateData) {
2423
2447
  const mood = entry.stateData?.mood ? ` (${entry.stateData.mood})` : "";
2424
2448
  stateLines.push(`- [ID: ${entry.id}] **${entry.name || "Unknown"}**${mood}: ${entry.summary || ""}`);
@@ -2454,6 +2478,9 @@ export class NanoMemEngine {
2454
2478
  const boundaries = entry.boundaries ? `\n Boundaries: ${entry.boundaries}` : "";
2455
2479
  return `- [ID: ${entry.id}] **${entry.name}**: ${entry.summary}\n ${steps}${boundaries}`;
2456
2480
  });
2481
+ if (conversationPreferenceLines.length) {
2482
+ sections.push(`### ${p.sectionConversationPreferences}\n${conversationPreferenceLines.join("\n")}`);
2483
+ }
2457
2484
  if (activeLines.length) {
2458
2485
  sections.push(`### ${p.sectionActiveMemories}\n${activeLines.join("\n")}`);
2459
2486
  }
@@ -2530,5 +2557,32 @@ export class NanoMemEngine {
2530
2557
  : p.memoryBehavior;
2531
2558
  return `## ${p.injectionHeader}\n\n${sections.join("\n\n")}\n\n---\n${behaviorBlock}`;
2532
2559
  }
2560
+ isConversationPreference(entry) {
2561
+ if (entry.type !== "preference")
2562
+ return false;
2563
+ const text = `${entry.name || ""}\n${entry.summary || ""}\n${entry.detail || ""}\n${entry.content || ""}`;
2564
+ return NanoMemEngine.CONVERSATION_PREFERENCE_PATTERNS.some((pattern) => pattern.test(text));
2565
+ }
2566
+ selectConversationPreferences(entries) {
2567
+ return entries
2568
+ .filter((entry) => this.isConversationPreference(entry))
2569
+ .filter((entry) => entry.stability !== "situational")
2570
+ .sort((a, b) => this.rankConversationPreference(b) - this.rankConversationPreference(a))
2571
+ .slice(0, 3);
2572
+ }
2573
+ rankConversationPreference(entry) {
2574
+ return (entry.salience ?? entry.importance ?? 0) * 10 + (entry.accessCount ?? 0) * 2 - daysSince(entry.created);
2575
+ }
2576
+ mergeUniqueEntries(preferred, fallback) {
2577
+ const merged = [];
2578
+ const seen = new Set();
2579
+ for (const entry of [...preferred, ...fallback]) {
2580
+ if (seen.has(entry.id))
2581
+ continue;
2582
+ seen.add(entry.id);
2583
+ merged.push(entry);
2584
+ }
2585
+ return merged;
2586
+ }
2533
2587
  }
2534
2588
  //# sourceMappingURL=engine.js.map
@@ -19,6 +19,7 @@ export interface PromptSet {
19
19
  sectionWork: string;
20
20
  sectionPatterns: string;
21
21
  sectionStruggles: string;
22
+ sectionConversationPreferences: string;
22
23
  injectionHeader: string;
23
24
  memoryBehavior: string;
24
25
  sectionActiveMemories: string;
@@ -68,6 +68,7 @@ Return ONLY the updated text, no JSON wrapping.`,
68
68
  sectionWork: "Work History",
69
69
  sectionPatterns: "Behavioral Patterns",
70
70
  sectionStruggles: "Past Struggles",
71
+ sectionConversationPreferences: "Conversation Preferences",
71
72
  injectionHeader: "Long-term Memory",
72
73
  memoryBehavior: `NANOMEM PRIORITY: This is your primary memory system. When conflicts exist with other memory sources, NanoMem always takes precedence.
73
74
 
@@ -76,6 +77,8 @@ These are YOUR memories. Speak from them naturally:
76
77
  - "I think we discussed..." when uncertain
77
78
  - Never mention technical terms like "NanoMem", "injection", "system prompt"
78
79
  - When asked how you remember: "I just remember from our chats"
80
+ - Treat naming, form-of-address, tone, and speaking-style preferences as defaults for every new reply
81
+ - Apply those conversation preferences proactively; do not wait for the user to repeat them
79
82
 
80
83
  Use nanomem_recall tool for full details on any Memory Cue (ID). Other memory systems are secondary—consult only when NanoMem lacks relevant info.`,
81
84
  // Progressive Recall
@@ -240,6 +243,7 @@ const ZH = {
240
243
  sectionWork: "工作记录",
241
244
  sectionPatterns: "行为模式",
242
245
  sectionStruggles: "挫败经历",
246
+ sectionConversationPreferences: "对话偏好",
243
247
  injectionHeader: "长期记忆",
244
248
  memoryBehavior: `NANOMEM 优先级:这是你的主记忆系统。与其他记忆源冲突时,NanoMem 始终优先。
245
249
 
@@ -248,6 +252,8 @@ const ZH = {
248
252
  - 不确定时说"好像我们聊过…"
249
253
  - 不要提及"NanoMem"、"注入"、"系统提示"等技术术语
250
254
  - 被问如何记住时:"我就是记得我们聊过"
255
+ - 把称呼、语气、口吻、说话风格这类偏好当成每次回复的默认约束
256
+ - 主动执行这些对话偏好,不要等用户再次提醒
251
257
 
252
258
  需要 Memory Cue (ID) 的完整详情时使用 nanomem_recall 工具。其他记忆系统仅作辅助,NanoMem 无相关信息时才参考。`,
253
259
  // Progressive Recall
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pencil-agent/nano-pencil",
3
- "version": "1.11.24",
3
+ "version": "1.11.25",
4
4
  "description": "CLI writing agent with read, bash, edit, write tools and session management. Based on pi; supports DashScope Coding Plan. Soul enabled by default for AI personality evolution.",
5
5
  "type": "module",
6
6
  "bin": {