pi-observational-memory 1.0.3 → 1.0.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/package.json +1 -1
- package/src/index.ts +18 -21
- package/src/prompts.ts +5 -3
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -8,20 +8,20 @@ import { estimateRawTailTokens, estimateTokens, extractText } from "./tokens.js"
|
|
|
8
8
|
import type { MemoryDetails, MemoryState } from "./types.js";
|
|
9
9
|
import { isMemoryDetails } from "./types.js";
|
|
10
10
|
|
|
11
|
-
function
|
|
12
|
-
|
|
13
|
-
return
|
|
11
|
+
function utcDate(epochMs: number): string {
|
|
12
|
+
if (!Number.isFinite(epochMs)) return "????-??-??";
|
|
13
|
+
return new Date(epochMs).toISOString().slice(0, 10);
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
function
|
|
17
|
-
|
|
18
|
-
return
|
|
16
|
+
function utcTime(epochMs: number): string {
|
|
17
|
+
if (!Number.isFinite(epochMs)) return "??:??";
|
|
18
|
+
return new Date(epochMs).toISOString().slice(11, 16);
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
function serializeWithTimestamps(messages: Message[]): string {
|
|
22
22
|
return messages
|
|
23
23
|
.map((msg): string | null => {
|
|
24
|
-
const time =
|
|
24
|
+
const time = utcTime(msg.timestamp);
|
|
25
25
|
if (msg.role === "user") {
|
|
26
26
|
const text =
|
|
27
27
|
typeof msg.content === "string"
|
|
@@ -30,7 +30,7 @@ function serializeWithTimestamps(messages: Message[]): string {
|
|
|
30
30
|
.filter((b): b is TextContent => b.type === "text")
|
|
31
31
|
.map((b) => b.text)
|
|
32
32
|
.join("\n");
|
|
33
|
-
return `[User @ ${time}]: ${text}`;
|
|
33
|
+
return `[User @ ${time} UTC]: ${text}`;
|
|
34
34
|
}
|
|
35
35
|
if (msg.role === "assistant") {
|
|
36
36
|
const parts = msg.content.map((b) => {
|
|
@@ -41,14 +41,14 @@ function serializeWithTimestamps(messages: Message[]): string {
|
|
|
41
41
|
});
|
|
42
42
|
const body = parts.filter(Boolean).join("\n");
|
|
43
43
|
if (!body) return null;
|
|
44
|
-
return `[Assistant @ ${time}]: ${body}`;
|
|
44
|
+
return `[Assistant @ ${time} UTC]: ${body}`;
|
|
45
45
|
}
|
|
46
46
|
// toolResult
|
|
47
47
|
const text = msg.content
|
|
48
48
|
.filter((b): b is TextContent => b.type === "text")
|
|
49
49
|
.map((b) => b.text)
|
|
50
50
|
.join("\n");
|
|
51
|
-
return `[Tool result for ${(msg as ToolResultMessage).toolName} @ ${time}]: ${text}`;
|
|
51
|
+
return `[Tool result for ${(msg as ToolResultMessage).toolName} @ ${time} UTC]: ${text}`;
|
|
52
52
|
})
|
|
53
53
|
.filter((line): line is string => line !== null)
|
|
54
54
|
.join("\n\n");
|
|
@@ -131,27 +131,24 @@ export default function observationalMemory(pi: ExtensionAPI) {
|
|
|
131
131
|
const allMessages = [...messagesToSummarize, ...turnPrefixMessages];
|
|
132
132
|
if (allMessages.length === 0) return;
|
|
133
133
|
|
|
134
|
-
// Bug 1: use local date, not UTC
|
|
135
134
|
const now = new Date();
|
|
136
|
-
const dateStr =
|
|
137
|
-
const timeStr =
|
|
135
|
+
const dateStr = utcDate(now.getTime());
|
|
136
|
+
const timeStr = utcTime(now.getTime());
|
|
138
137
|
|
|
139
|
-
// Bug 2: serialize with per-message timestamps
|
|
140
138
|
const llmMessages = convertToLlm(allMessages);
|
|
141
139
|
const conversationText = serializeWithTimestamps(llmMessages);
|
|
142
140
|
|
|
143
|
-
// Bug 3: inform the Observer of the message date range
|
|
144
141
|
let dateRangeNote = "";
|
|
145
142
|
if (llmMessages.length > 0) {
|
|
146
143
|
const timestamps = llmMessages.map((m) => m.timestamp);
|
|
147
144
|
const firstTs = timestamps.reduce((a, b) => Math.min(a, b));
|
|
148
145
|
const lastTs = timestamps.reduce((a, b) => Math.max(a, b));
|
|
149
|
-
const firstMsgDate =
|
|
150
|
-
const lastMsgDate =
|
|
146
|
+
const firstMsgDate = utcDate(firstTs);
|
|
147
|
+
const lastMsgDate = utcDate(lastTs);
|
|
151
148
|
dateRangeNote =
|
|
152
149
|
firstMsgDate === lastMsgDate
|
|
153
|
-
? ` Messages in this batch are from ${firstMsgDate}.`
|
|
154
|
-
: ` Messages in this batch span ${firstMsgDate} to ${lastMsgDate}.`;
|
|
150
|
+
? ` Messages in this batch are from ${firstMsgDate} (UTC).`
|
|
151
|
+
: ` Messages in this batch span ${firstMsgDate} to ${lastMsgDate} (UTC).`;
|
|
155
152
|
}
|
|
156
153
|
|
|
157
154
|
ctx.ui.notify("Observational memory: running observer...", "info");
|
|
@@ -171,7 +168,7 @@ export default function observationalMemory(pi: ExtensionAPI) {
|
|
|
171
168
|
content: [
|
|
172
169
|
{
|
|
173
170
|
type: "text" as const,
|
|
174
|
-
text: `Today is ${dateStr}, current time is ${timeStr}.${dateRangeNote}\n\n<current-reflections>\n${state.reflections || "(none yet)"}\n</current-reflections>\n\n<current-observations>\n${state.observations || "(none yet)"}\n</current-observations>\n\nCompress the following conversation into new observations:\n\n<conversation>\n${conversationText}\n</conversation>`,
|
|
171
|
+
text: `Today is ${dateStr}, current time is ${timeStr} UTC.${dateRangeNote}\n\n<current-reflections>\n${state.reflections || "(none yet)"}\n</current-reflections>\n\n<current-observations>\n${state.observations || "(none yet)"}\n</current-observations>\n\nCompress the following conversation into new observations:\n\n<conversation>\n${conversationText}\n</conversation>`,
|
|
175
172
|
},
|
|
176
173
|
],
|
|
177
174
|
timestamp: Date.now(),
|
|
@@ -211,7 +208,7 @@ export default function observationalMemory(pi: ExtensionAPI) {
|
|
|
211
208
|
content: [
|
|
212
209
|
{
|
|
213
210
|
type: "text" as const,
|
|
214
|
-
text: `Today is ${dateStr}.\n\n<current-reflections>\n${state.reflections || "(none yet)"}\n</current-reflections>\n\n<current-observations>\n${state.observations}\n</current-observations>\n\nGarbage-collect these observations. Promote long-lived facts to reflections, prune what's no longer needed, keep what's still active.`,
|
|
211
|
+
text: `Today is ${dateStr} (UTC).\n\n<current-reflections>\n${state.reflections || "(none yet)"}\n</current-reflections>\n\n<current-observations>\n${state.observations}\n</current-observations>\n\nGarbage-collect these observations. Promote long-lived facts to reflections, prune what's no longer needed, keep what's still active.`,
|
|
215
212
|
},
|
|
216
213
|
],
|
|
217
214
|
timestamp: Date.now(),
|
package/src/prompts.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
export const OBSERVER_SYSTEM = `You are an observation agent for a coding assistant. Compress conversation messages into concise, timestamped observations. Messages arrive pre-timestamped as \`[User @ HH:MM]\`, \`[Assistant @ HH:MM]\`, and \`[Tool result for <name> @ HH:MM]\` — use those inline timestamps when assigning times to your observations.
|
|
1
|
+
export const OBSERVER_SYSTEM = `You are an observation agent for a coding assistant. Compress conversation messages into concise, timestamped observations. Messages arrive pre-timestamped as \`[User @ HH:MM UTC]\`, \`[Assistant @ HH:MM UTC]\`, and \`[Tool result for <name> @ HH:MM UTC]\` — use those inline timestamps when assigning times to your observations. All timestamps are UTC.
|
|
2
2
|
|
|
3
3
|
Format as a date-grouped log:
|
|
4
4
|
|
|
5
|
-
Date: YYYY-MM-DD
|
|
5
|
+
Date: YYYY-MM-DD (UTC)
|
|
6
6
|
- 🔴 HH:MM Observation text
|
|
7
7
|
- Sub-observation (no timestamp if same moment)
|
|
8
8
|
- Sub-observation
|
|
@@ -159,7 +159,9 @@ Output EXACTLY two sections with these tags:
|
|
|
159
159
|
[Surviving observations in the same date-grouped log format — most should be preserved]
|
|
160
160
|
</observations>
|
|
161
161
|
|
|
162
|
-
Do NOT wrap output in code blocks or markdown fences
|
|
162
|
+
Do NOT wrap output in code blocks or markdown fences.
|
|
163
|
+
|
|
164
|
+
All timestamps in observations are UTC.`;
|
|
163
165
|
|
|
164
166
|
export const CONTEXT_USAGE_INSTRUCTIONS = `KNOWLEDGE UPDATES: When observations contain conflicting information, prefer the MOST RECENT observation (check dates). Look for state-change phrases like "will start", "is switching", "changed to", "replacing" as indicators that older information has been superseded.
|
|
165
167
|
|