@ppagent/memory 0.4.2 → 0.4.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -159,6 +159,7 @@ function tableDefs(dim) {
159
159
  { name: "parts", type: "text", nullable: true },
160
160
  // 兼容存量数据
161
161
  { name: "payload", type: "text", nullable: true },
162
+ { name: "context_payload", type: "text", nullable: true },
162
163
  { name: "vector", type: "vector" },
163
164
  { name: "usage", type: "int" },
164
165
  { name: "metadata", type: "text" },
@@ -290,6 +291,7 @@ function messageToRow(m) {
290
291
  content: m.content,
291
292
  parts: m.parts ?? "[]",
292
293
  payload: m.payload ?? null,
294
+ context_payload: m.contextPayload ?? null,
293
295
  vector: m.vector,
294
296
  usage: m.usage,
295
297
  metadata: m.metadata,
@@ -308,6 +310,7 @@ function rowToMessage(r) {
308
310
  parts: r.parts ?? "[]",
309
311
  // 旧数据无此列时安全降级
310
312
  payload: r.payload ?? void 0,
313
+ contextPayload: r.context_payload ?? void 0,
311
314
  vector: toVector(r.vector),
312
315
  usage: positiveNumber(r.usage),
313
316
  metadata: r.metadata,
@@ -594,7 +597,13 @@ var MemoryStore = class {
594
597
  report.messagesScanned = messageRows.length;
595
598
  for (const row of messageRows) {
596
599
  const usage2 = countTokens(
597
- [row.content, row.parts ?? "[]", row.metadata ?? "{}", row.payload ?? ""].map(String).join("\n")
600
+ [
601
+ row.content,
602
+ row.parts ?? "[]",
603
+ row.metadata ?? "{}",
604
+ row.payload ?? "",
605
+ row.context_payload ?? ""
606
+ ].map(String).join("\n")
598
607
  );
599
608
  if (Number(row.usage) === usage2) continue;
600
609
  await this.provider.update(MESSAGES_TABLE, { usage: usage2 }, [
package/dist/index.d.ts CHANGED
@@ -61,6 +61,12 @@ interface RawMessage {
61
61
  parts?: ContentPart[];
62
62
  /** 宿主框架的原始消息载荷;记忆框架仅透明保存和回读。 */
63
63
  payload?: unknown;
64
+ /**
65
+ * 仅用于下一次模型上下文重放的宿主载荷。
66
+ * 会透明保存、原样回读并计入原始窗口 token,但不会进入 embedding、全文检索、
67
+ * Topic 摘要或知识图谱抽取。适合保存 provider-ready 工具协议消息等大体积细节。
68
+ */
69
+ contextPayload?: unknown;
64
70
  usage?: number;
65
71
  metadata?: Record<string, unknown>;
66
72
  createdAt?: number;
@@ -78,6 +84,8 @@ interface StoredMessage {
78
84
  parts: string;
79
85
  /** JSON.stringify(payload),未提供时为 undefined。 */
80
86
  payload?: string;
87
+ /** JSON.stringify(contextPayload),未提供时为 undefined。 */
88
+ contextPayload?: string;
81
89
  usage: number;
82
90
  metadata: string;
83
91
  vector: number[];
@@ -236,6 +244,7 @@ interface MemoryRawMessage {
236
244
  content: string;
237
245
  parts?: ContentPart[];
238
246
  payload?: unknown;
247
+ contextPayload?: unknown;
239
248
  usage: number;
240
249
  metadata?: Record<string, unknown>;
241
250
  createdAt: number;
@@ -261,6 +270,11 @@ interface GetHistoryWindowOptions {
261
270
  /** 固定预算的压缩记忆 + 近期未压缩原始消息。 */
262
271
  interface MemoryContextWindow {
263
272
  sessionId: string;
273
+ /**
274
+ * 当前压缩边界的轻量版本标识。后台/阻塞压缩或 Topic 归并改变窗口时随之变化;
275
+ * 新增原始消息本身不保证改变该值。
276
+ */
277
+ compressionRevision?: string;
264
278
  compressedContext: string;
265
279
  recentMessages: MemoryRawMessage[];
266
280
  usage: MemoryContextWindowUsage;
package/dist/index.js CHANGED
@@ -827,6 +827,7 @@ function tableDefs(dim) {
827
827
  { name: "parts", type: "text", nullable: true },
828
828
  // 兼容存量数据
829
829
  { name: "payload", type: "text", nullable: true },
830
+ { name: "context_payload", type: "text", nullable: true },
830
831
  { name: "vector", type: "vector" },
831
832
  { name: "usage", type: "int" },
832
833
  { name: "metadata", type: "text" },
@@ -958,6 +959,7 @@ function messageToRow(m) {
958
959
  content: m.content,
959
960
  parts: m.parts ?? "[]",
960
961
  payload: m.payload ?? null,
962
+ context_payload: m.contextPayload ?? null,
961
963
  vector: m.vector,
962
964
  usage: m.usage,
963
965
  metadata: m.metadata,
@@ -976,6 +978,7 @@ function rowToMessage(r) {
976
978
  parts: r.parts ?? "[]",
977
979
  // 旧数据无此列时安全降级
978
980
  payload: r.payload ?? void 0,
981
+ contextPayload: r.context_payload ?? void 0,
979
982
  vector: toVector(r.vector),
980
983
  usage: positiveNumber(r.usage),
981
984
  metadata: r.metadata,
@@ -1262,7 +1265,13 @@ var MemoryStore = class {
1262
1265
  report.messagesScanned = messageRows.length;
1263
1266
  for (const row of messageRows) {
1264
1267
  const usage = countTokens(
1265
- [row.content, row.parts ?? "[]", row.metadata ?? "{}", row.payload ?? ""].map(String).join("\n")
1268
+ [
1269
+ row.content,
1270
+ row.parts ?? "[]",
1271
+ row.metadata ?? "{}",
1272
+ row.payload ?? "",
1273
+ row.context_payload ?? ""
1274
+ ].map(String).join("\n")
1266
1275
  );
1267
1276
  if (Number(row.usage) === usage) continue;
1268
1277
  await this.provider.update(MESSAGES_TABLE, { usage }, [
@@ -3143,7 +3152,7 @@ function effectiveUsage(message) {
3143
3152
  if (Number.isFinite(message.usage) && message.usage > 0) return Math.floor(message.usage);
3144
3153
  return Math.max(
3145
3154
  1,
3146
- (message.content.length + (message.parts?.length ?? 0) + message.metadata.length + (message.payload?.length ?? 0)) * 2
3155
+ (message.content.length + (message.parts?.length ?? 0) + message.metadata.length + (message.payload?.length ?? 0) + (message.contextPayload?.length ?? 0)) * 2
3147
3156
  );
3148
3157
  }
3149
3158
  function sumMessageTokens(messages) {
@@ -3334,7 +3343,8 @@ var MemoryManager = class {
3334
3343
  const parts = JSON.stringify(message.parts ?? []);
3335
3344
  const metadata = JSON.stringify(message.metadata ?? {});
3336
3345
  const payload = message.payload === void 0 ? void 0 : JSON.stringify(message.payload);
3337
- const tokenInput = [message.content, parts, metadata, payload ?? ""].join("\n");
3346
+ const contextPayload = message.contextPayload === void 0 ? void 0 : JSON.stringify(message.contextPayload);
3347
+ const tokenInput = [message.content, parts, metadata, payload ?? "", contextPayload ?? ""].join("\n");
3338
3348
  return {
3339
3349
  messageId: message.messageId ?? uuidv44(),
3340
3350
  talkerId: message.talkerId ?? "user",
@@ -3345,6 +3355,7 @@ var MemoryManager = class {
3345
3355
  content: message.content,
3346
3356
  parts,
3347
3357
  payload,
3358
+ contextPayload,
3348
3359
  usage: message.usage ?? countTokens(tokenInput),
3349
3360
  metadata,
3350
3361
  createdAt: message.createdAt ?? now
@@ -3625,6 +3636,7 @@ var MemoryManager = class {
3625
3636
  }
3626
3637
  return {
3627
3638
  sessionId,
3639
+ compressionRevision: buildCompressionRevision(topics, entry.messages),
3628
3640
  compressedContext: this.sessionCache.buildCompressedContext(sessionId, topics),
3629
3641
  recentMessages: entry.messages.map(toMemoryRawMessage),
3630
3642
  usage: {
@@ -3885,6 +3897,7 @@ var MemoryManager = class {
3885
3897
  function toMemoryRawMessage(message) {
3886
3898
  const parts = safeParseArray(message.parts);
3887
3899
  const payload = message.payload === void 0 ? void 0 : safeParseValue(message.payload);
3900
+ const contextPayload = message.contextPayload === void 0 ? void 0 : safeParseValue(message.contextPayload);
3888
3901
  return {
3889
3902
  messageId: message.messageId,
3890
3903
  talkerId: message.talkerId,
@@ -3895,11 +3908,22 @@ function toMemoryRawMessage(message) {
3895
3908
  content: message.content,
3896
3909
  ...parts.length > 0 && { parts },
3897
3910
  ...payload !== void 0 && { payload },
3911
+ ...contextPayload !== void 0 && { contextPayload },
3898
3912
  usage: message.usage,
3899
3913
  metadata: safeParseObject2(message.metadata),
3900
3914
  createdAt: message.createdAt
3901
3915
  };
3902
3916
  }
3917
+ function buildCompressionRevision(topics, messages) {
3918
+ const lastTopic = topics.at(-1);
3919
+ const firstRaw = messages.at(0);
3920
+ return [
3921
+ topics.length,
3922
+ lastTopic?.summaryId ?? "none",
3923
+ lastTopic?.updatedAt ?? 0,
3924
+ firstRaw?.messageId ?? "none"
3925
+ ].join(":");
3926
+ }
3903
3927
  function sumTopicTokens2(topics) {
3904
3928
  return topics.reduce((sum, topic) => sum + Math.max(1, topic.tokens), 0);
3905
3929
  }
package/llms.txt CHANGED
@@ -374,7 +374,8 @@ The package intentionally separates durable base writes from expensive graph con
374
374
  `updateChat(messages, opts)`:
375
375
 
376
376
  - Registers its write synchronously, so a caller may intentionally fire-and-forget it and a following `getHistoryWindow` will still wait for that write.
377
- - Calculates tokens over `content`, `parts`, `payload`, and `metadata`, embeds the searchable text, and upserts raw messages by stable `messageId` before resolving.
377
+ - Calculates tokens over `content`, `parts`, `payload`, `contextPayload`, and `metadata`, embeds only the searchable text, and upserts raw messages by stable `messageId` before resolving.
378
+ - `contextPayload` is a replay-only host payload: it is stored and returned unchanged and counts toward the raw budget, but is excluded from embedding, FTS, Topic-summary input, and conversation graph extraction.
378
379
  - Raw-message token usage always counts the complete stored `metadata`. When a raw batch is summarized, common tool-call/tool-result fields are removed only from the temporary LLM compression input; persisted metadata and uncompressed `recentMessages` remain unchanged.
379
380
  - Creates or updates the session record.
380
381
  - Updates the in-memory session cache.
@@ -393,7 +394,7 @@ The package intentionally separates durable base writes from expensive graph con
393
394
 
394
395
  - Lazily hydrates the session from persistent Topics plus raw messages after the newest Topic boundary.
395
396
  - Waits for registered message writes. If the hard raw budget is exceeded, it also waits for or starts compression until the returned context fits.
396
- - Returns `{ compressedContext, recentMessages, usage }`. `recentMessages` preserves the `RawMessage` input shape, including `messageId`, `parts`, `payload`, `metadata`, and `createdAt`.
397
+ - Returns `{ compressionRevision, compressedContext, recentMessages, usage }`. `recentMessages` preserves the `RawMessage` input shape, including `messageId`, `parts`, `payload`, `contextPayload`, `metadata`, and `createdAt`. `compressionRevision` changes when the returned Topic/raw compression boundary changes and is intended for host observability rather than optimistic locking.
397
398
  - The effective Topic budget is `min(compressedContextTokenLimit, usableContextTokens * compressedContextRatio)`; the remaining usable history budget is reserved for raw messages.
398
399
  - Cold hydration always restores all persisted Topics and never silently truncates them. Each rollup summarizes the oldest approximately half of the current Topic tokens; a single oversized Topic is re-summarized by itself.
399
400
  - A small Topic-only overage returns immediately and schedules a transient background rollup for the next read. It blocks only when the overage exceeds `topicCompactionSyncRatio` or Topic plus raw history cannot fit the usable window.
@@ -425,6 +426,7 @@ Use `wait: true` when the next line of code must immediately call `searchKnowled
425
426
  - `content: string` - required plain text used for embedding and retrieval.
426
427
  - `parts?: ContentPart[]` - optional multimodal content parts; stored serialized.
427
428
  - `payload?: unknown` - host-framework message payload stored and returned without interpretation.
429
+ - `contextPayload?: unknown` - replay-only host context stored and returned without interpretation. It counts toward the raw history budget but is excluded from retrieval, compression summaries, and graph extraction.
428
430
  - `usage?: number` - token count; estimated with `tiktoken` if omitted.
429
431
  - `metadata?: Record<string, unknown>` - custom metadata stored as JSON.
430
432
  - `createdAt?: number` - Unix milliseconds; default is current time.
@@ -618,8 +620,9 @@ Context:
618
620
 
619
621
  Returns the complete model-history window:
620
622
 
623
+ - `compressionRevision?: string` - lightweight identifier for the current Topic/raw compression boundary. Background or blocking compression changes it; ordinary raw appends are not guaranteed to do so.
621
624
  - `compressedContext: string` - chronological Topic summaries for the current model-size budget. A soft cold-start overage may be returned once while its transient rollup runs in the background.
622
- - `recentMessages: MemoryRawMessage[]` - all currently uncompressed raw messages in chronological order, preserving the host payload and metadata.
625
+ - `recentMessages: MemoryRawMessage[]` - all currently uncompressed raw messages in chronological order, preserving the host payload, replay-only context payload, and metadata.
623
626
  - `usage` - model size, usable history size, compressed usage, and dynamic raw-message budget/usage.
624
627
 
625
628
  Pass the active model's context size on every read. Omitting it uses `defaultModelContextTokens` (256K by default) and logs a warning.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ppagent/memory",
3
- "version": "0.4.2",
3
+ "version": "0.4.4",
4
4
  "description": "独立记忆系统模块,向量存储支持 LanceDB / SQLite(sqlite-vec) 双后端自动切换 + Grafeo 知识图谱",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",