@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 +10 -1
- package/dist/index.d.ts +14 -0
- package/dist/index.js +27 -3
- package/llms.txt +6 -3
- package/package.json +1 -1
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
|
-
[
|
|
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
|
-
[
|
|
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
|
|
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.
|