@ddwang/magnitude-core 0.3.1-ddwang.6 → 0.3.1-ddwang.7
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/agent/index.js +6 -1
- package/dist/index.cjs +21 -4
- package/dist/index.d.cts +3 -0
- package/dist/index.mjs +21 -4
- package/dist/memory/agentMemory.d.ts +3 -0
- package/dist/memory/agentMemory.js +18 -10
- package/dist/memory/agentMemory.test.js +65 -2
- package/package.json +1 -1
package/dist/agent/index.js
CHANGED
|
@@ -325,7 +325,12 @@ export class Agent {
|
|
|
325
325
|
...(this.options.prompt ? [this.options.prompt] : []),
|
|
326
326
|
...(options.prompt ? [options.prompt] : []),
|
|
327
327
|
].join('\n');
|
|
328
|
-
const taskMemory = options.memory ?? new AgentMemory(
|
|
328
|
+
const taskMemory = options.memory ?? new AgentMemory();
|
|
329
|
+
taskMemory.configure({
|
|
330
|
+
...this.memoryOptions,
|
|
331
|
+
// Current prompts replace checkpoint instructions; omitted prompts preserve them.
|
|
332
|
+
...(this.options.prompt != null || options.prompt !== undefined ? { instructions: instructions || null } : {}),
|
|
333
|
+
});
|
|
329
334
|
if (Array.isArray(taskOrSteps)) {
|
|
330
335
|
const steps = taskOrSteps;
|
|
331
336
|
//this.events.emit('actStarted', steps.join(', '));
|
package/dist/index.cjs
CHANGED
|
@@ -1555,6 +1555,15 @@ class AgentMemory {
|
|
|
1555
1555
|
get instructions() {
|
|
1556
1556
|
return this.options.instructions;
|
|
1557
1557
|
}
|
|
1558
|
+
/** Apply current instructions and provider caching policy without replacing task evidence. */
|
|
1559
|
+
configure(options) {
|
|
1560
|
+
if (options.instructions !== void 0 && options.instructions !== this.options.instructions || options.promptCaching !== void 0 && options.promptCaching !== this.options.promptCaching) {
|
|
1561
|
+
this.freezeMask = void 0;
|
|
1562
|
+
this.cacheControlIndices = [];
|
|
1563
|
+
}
|
|
1564
|
+
if (options.instructions !== void 0) this.options.instructions = options.instructions;
|
|
1565
|
+
if (options.promptCaching !== void 0) this.options.promptCaching = options.promptCaching;
|
|
1566
|
+
}
|
|
1558
1567
|
async render(options) {
|
|
1559
1568
|
if (options?.history === "full") {
|
|
1560
1569
|
const messages2 = [];
|
|
@@ -1657,14 +1666,16 @@ class AgentMemory {
|
|
|
1657
1666
|
}
|
|
1658
1667
|
const notes = this.notebook.toJSON();
|
|
1659
1668
|
return {
|
|
1660
|
-
|
|
1661
|
-
...this.options.instructions ? { instructions: this.options.instructions } : {},
|
|
1669
|
+
...this.options.instructions !== null ? { instructions: this.options.instructions } : {},
|
|
1662
1670
|
...notes.length ? { notes } : {},
|
|
1663
1671
|
observations
|
|
1664
1672
|
};
|
|
1665
1673
|
}
|
|
1666
|
-
|
|
1674
|
+
/** Replace saved state atomically; runtime caching and thought limits stay with this instance. */
|
|
1667
1675
|
async loadJSON(data) {
|
|
1676
|
+
if (data.instructions !== void 0 && typeof data.instructions !== "string") {
|
|
1677
|
+
throw new Error("Checkpoint instructions must be a string");
|
|
1678
|
+
}
|
|
1668
1679
|
const observations = [];
|
|
1669
1680
|
for (const observation of data.observations) {
|
|
1670
1681
|
observations.push(new Observation(
|
|
@@ -1677,6 +1688,7 @@ class AgentMemory {
|
|
|
1677
1688
|
}
|
|
1678
1689
|
const notebook = new TaskNotebook();
|
|
1679
1690
|
for (const note of data.notes ?? []) notebook.put(note, (id) => this.resolveNoteSource(observations, id));
|
|
1691
|
+
this.options.instructions = data.instructions ?? null;
|
|
1680
1692
|
this.observations = observations;
|
|
1681
1693
|
this.notebook = notebook;
|
|
1682
1694
|
this.visibleSourceIds.clear();
|
|
@@ -4261,7 +4273,12 @@ class Agent {
|
|
|
4261
4273
|
...this.options.prompt ? [this.options.prompt] : [],
|
|
4262
4274
|
...options.prompt ? [options.prompt] : []
|
|
4263
4275
|
].join("\n");
|
|
4264
|
-
const taskMemory = options.memory ?? new AgentMemory(
|
|
4276
|
+
const taskMemory = options.memory ?? new AgentMemory();
|
|
4277
|
+
taskMemory.configure({
|
|
4278
|
+
...this.memoryOptions,
|
|
4279
|
+
// Current prompts replace checkpoint instructions; omitted prompts preserve them.
|
|
4280
|
+
...this.options.prompt != null || options.prompt !== void 0 ? { instructions: instructions || null } : {}
|
|
4281
|
+
});
|
|
4265
4282
|
if (Array.isArray(taskOrSteps)) {
|
|
4266
4283
|
const steps = taskOrSteps;
|
|
4267
4284
|
await traceAsync("multistep", async (steps2, options2) => {
|
package/dist/index.d.cts
CHANGED
|
@@ -455,6 +455,8 @@ declare class AgentMemory {
|
|
|
455
455
|
private cacheControlIndices;
|
|
456
456
|
constructor(options?: AgentMemoryOptions);
|
|
457
457
|
get instructions(): string | null;
|
|
458
|
+
/** Apply current instructions and provider caching policy without replacing task evidence. */
|
|
459
|
+
configure(options: Pick<AgentMemoryOptions, 'instructions' | 'promptCaching'>): void;
|
|
458
460
|
render(options?: MemoryRenderOptions): Promise<MultiMediaMessage[]>;
|
|
459
461
|
simpleRender(): Promise<(Image$1 | string)[]>;
|
|
460
462
|
private observationPrefix;
|
|
@@ -466,6 +468,7 @@ declare class AgentMemory {
|
|
|
466
468
|
recordObservation(obs: Observation): void;
|
|
467
469
|
getLastThoughtMessage(): string | null;
|
|
468
470
|
toJSON(): Promise<SerializedAgentMemory>;
|
|
471
|
+
/** Replace saved state atomically; runtime caching and thought limits stay with this instance. */
|
|
469
472
|
loadJSON(data: SerializedAgentMemory): Promise<void>;
|
|
470
473
|
}
|
|
471
474
|
|
package/dist/index.mjs
CHANGED
|
@@ -1532,6 +1532,15 @@ class AgentMemory {
|
|
|
1532
1532
|
get instructions() {
|
|
1533
1533
|
return this.options.instructions;
|
|
1534
1534
|
}
|
|
1535
|
+
/** Apply current instructions and provider caching policy without replacing task evidence. */
|
|
1536
|
+
configure(options) {
|
|
1537
|
+
if (options.instructions !== void 0 && options.instructions !== this.options.instructions || options.promptCaching !== void 0 && options.promptCaching !== this.options.promptCaching) {
|
|
1538
|
+
this.freezeMask = void 0;
|
|
1539
|
+
this.cacheControlIndices = [];
|
|
1540
|
+
}
|
|
1541
|
+
if (options.instructions !== void 0) this.options.instructions = options.instructions;
|
|
1542
|
+
if (options.promptCaching !== void 0) this.options.promptCaching = options.promptCaching;
|
|
1543
|
+
}
|
|
1535
1544
|
async render(options) {
|
|
1536
1545
|
if (options?.history === "full") {
|
|
1537
1546
|
const messages2 = [];
|
|
@@ -1634,14 +1643,16 @@ class AgentMemory {
|
|
|
1634
1643
|
}
|
|
1635
1644
|
const notes = this.notebook.toJSON();
|
|
1636
1645
|
return {
|
|
1637
|
-
|
|
1638
|
-
...this.options.instructions ? { instructions: this.options.instructions } : {},
|
|
1646
|
+
...this.options.instructions !== null ? { instructions: this.options.instructions } : {},
|
|
1639
1647
|
...notes.length ? { notes } : {},
|
|
1640
1648
|
observations
|
|
1641
1649
|
};
|
|
1642
1650
|
}
|
|
1643
|
-
|
|
1651
|
+
/** Replace saved state atomically; runtime caching and thought limits stay with this instance. */
|
|
1644
1652
|
async loadJSON(data) {
|
|
1653
|
+
if (data.instructions !== void 0 && typeof data.instructions !== "string") {
|
|
1654
|
+
throw new Error("Checkpoint instructions must be a string");
|
|
1655
|
+
}
|
|
1645
1656
|
const observations = [];
|
|
1646
1657
|
for (const observation of data.observations) {
|
|
1647
1658
|
observations.push(new Observation(
|
|
@@ -1654,6 +1665,7 @@ class AgentMemory {
|
|
|
1654
1665
|
}
|
|
1655
1666
|
const notebook = new TaskNotebook();
|
|
1656
1667
|
for (const note of data.notes ?? []) notebook.put(note, (id) => this.resolveNoteSource(observations, id));
|
|
1668
|
+
this.options.instructions = data.instructions ?? null;
|
|
1657
1669
|
this.observations = observations;
|
|
1658
1670
|
this.notebook = notebook;
|
|
1659
1671
|
this.visibleSourceIds.clear();
|
|
@@ -4238,7 +4250,12 @@ class Agent {
|
|
|
4238
4250
|
...this.options.prompt ? [this.options.prompt] : [],
|
|
4239
4251
|
...options.prompt ? [options.prompt] : []
|
|
4240
4252
|
].join("\n");
|
|
4241
|
-
const taskMemory = options.memory ?? new AgentMemory(
|
|
4253
|
+
const taskMemory = options.memory ?? new AgentMemory();
|
|
4254
|
+
taskMemory.configure({
|
|
4255
|
+
...this.memoryOptions,
|
|
4256
|
+
// Current prompts replace checkpoint instructions; omitted prompts preserve them.
|
|
4257
|
+
...this.options.prompt != null || options.prompt !== void 0 ? { instructions: instructions || null } : {}
|
|
4258
|
+
});
|
|
4242
4259
|
if (Array.isArray(taskOrSteps)) {
|
|
4243
4260
|
const steps = taskOrSteps;
|
|
4244
4261
|
await traceAsync("multistep", async (steps2, options2) => {
|
|
@@ -32,6 +32,8 @@ export declare class AgentMemory {
|
|
|
32
32
|
private cacheControlIndices;
|
|
33
33
|
constructor(options?: AgentMemoryOptions);
|
|
34
34
|
get instructions(): string | null;
|
|
35
|
+
/** Apply current instructions and provider caching policy without replacing task evidence. */
|
|
36
|
+
configure(options: Pick<AgentMemoryOptions, 'instructions' | 'promptCaching'>): void;
|
|
35
37
|
render(options?: MemoryRenderOptions): Promise<MultiMediaMessage[]>;
|
|
36
38
|
simpleRender(): Promise<(BamlImage | string)[]>;
|
|
37
39
|
private observationPrefix;
|
|
@@ -43,5 +45,6 @@ export declare class AgentMemory {
|
|
|
43
45
|
recordObservation(obs: Observation): void;
|
|
44
46
|
getLastThoughtMessage(): string | null;
|
|
45
47
|
toJSON(): Promise<SerializedAgentMemory>;
|
|
48
|
+
/** Replace saved state atomically; runtime caching and thought limits stay with this instance. */
|
|
46
49
|
loadJSON(data: SerializedAgentMemory): Promise<void>;
|
|
47
50
|
}
|
|
@@ -32,6 +32,18 @@ export class AgentMemory {
|
|
|
32
32
|
// why is this on memory? prob should just be on agent
|
|
33
33
|
return this.options.instructions;
|
|
34
34
|
}
|
|
35
|
+
/** Apply current instructions and provider caching policy without replacing task evidence. */
|
|
36
|
+
configure(options) {
|
|
37
|
+
if ((options.instructions !== undefined && options.instructions !== this.options.instructions)
|
|
38
|
+
|| (options.promptCaching !== undefined && options.promptCaching !== this.options.promptCaching)) {
|
|
39
|
+
this.freezeMask = undefined;
|
|
40
|
+
this.cacheControlIndices = [];
|
|
41
|
+
}
|
|
42
|
+
if (options.instructions !== undefined)
|
|
43
|
+
this.options.instructions = options.instructions;
|
|
44
|
+
if (options.promptCaching !== undefined)
|
|
45
|
+
this.options.promptCaching = options.promptCaching;
|
|
46
|
+
}
|
|
35
47
|
async render(options) {
|
|
36
48
|
if (options?.history === 'full') {
|
|
37
49
|
const messages = [];
|
|
@@ -145,32 +157,28 @@ export class AgentMemory {
|
|
|
145
157
|
}
|
|
146
158
|
const notes = this.notebook.toJSON();
|
|
147
159
|
return {
|
|
148
|
-
|
|
149
|
-
...(this.options.instructions ? { instructions: this.options.instructions } : {}),
|
|
160
|
+
...(this.options.instructions !== null ? { instructions: this.options.instructions } : {}),
|
|
150
161
|
...(notes.length ? { notes } : {}),
|
|
151
162
|
observations: observations
|
|
152
163
|
};
|
|
153
164
|
}
|
|
154
|
-
|
|
165
|
+
/** Replace saved state atomically; runtime caching and thought limits stay with this instance. */
|
|
155
166
|
async loadJSON(data) {
|
|
156
|
-
|
|
167
|
+
if (data.instructions !== undefined && typeof data.instructions !== 'string') {
|
|
168
|
+
throw new Error('Checkpoint instructions must be a string');
|
|
169
|
+
}
|
|
157
170
|
const observations = [];
|
|
158
171
|
for (const observation of data.observations) {
|
|
159
172
|
observations.push(new Observation(observation.source, observation.role, await jsonToObservableData(observation.data), observation.options, observation.timestamp));
|
|
160
173
|
}
|
|
161
|
-
// nvm
|
|
162
|
-
//this.instructions = this.instructions;
|
|
163
174
|
const notebook = new TaskNotebook();
|
|
164
175
|
for (const note of data.notes ?? [])
|
|
165
176
|
notebook.put(note, id => this.resolveNoteSource(observations, id));
|
|
177
|
+
this.options.instructions = data.instructions ?? null;
|
|
166
178
|
this.observations = observations;
|
|
167
179
|
this.notebook = notebook;
|
|
168
180
|
this.visibleSourceIds.clear();
|
|
169
181
|
this.freezeMask = undefined;
|
|
170
182
|
this.cacheControlIndices = [];
|
|
171
|
-
// return {
|
|
172
|
-
// ...(this.instructions ? { instructions: this.instructions } : {}),
|
|
173
|
-
// observations: observations
|
|
174
|
-
// };
|
|
175
183
|
}
|
|
176
184
|
}
|
|
@@ -9,6 +9,66 @@ const screen = (n) => Observation.fromConnector('fixture', {
|
|
|
9
9
|
}, { type: 'screenshot', limit: 2, dedupe: true });
|
|
10
10
|
const text = async (memory) => (await memory.render()).flatMap(message => message.content)
|
|
11
11
|
.filter(part => typeof part === 'string').join('');
|
|
12
|
+
for (const instructions of ['Saved planner constraint', '', undefined]) {
|
|
13
|
+
for (const promptCaching of [false, true])
|
|
14
|
+
test(`checkpoint instructions round-trip with receiving runtime configuration: ${JSON.stringify(instructions)}, caching ${promptCaching}`, async () => {
|
|
15
|
+
const original = new AgentMemory({ instructions, promptCaching: !promptCaching });
|
|
16
|
+
original.recordObservation(Observation.fromConnector('fixture', 'Saved evidence'));
|
|
17
|
+
await original.render();
|
|
18
|
+
original.remember({ key: 'saved', text: 'Saved fact', sources: [0] });
|
|
19
|
+
const saved = JSON.parse(JSON.stringify(await original.toJSON()));
|
|
20
|
+
const restored = new AgentMemory({ instructions: 'Previous task instructions', promptCaching });
|
|
21
|
+
await restored.loadJSON(saved);
|
|
22
|
+
expect(restored.instructions).toBe(instructions ?? null);
|
|
23
|
+
expect(await restored.toJSON()).toEqual(saved);
|
|
24
|
+
expect((await restored.render()).some(message => message.cacheControl)).toBe(promptCaching);
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
test('changing runtime caching discards frozen retention without changing the audit', async () => {
|
|
28
|
+
const memory = new AgentMemory({ instructions: 'Original constraint', promptCaching: true });
|
|
29
|
+
memory.recordObservation(Observation.fromConnector('fixture', 'Old state', { type: 'state', limit: 1 }));
|
|
30
|
+
await memory.render();
|
|
31
|
+
memory.recordObservation(Observation.fromConnector('fixture', 'New state', { type: 'state', limit: 1 }));
|
|
32
|
+
expect(await text(memory)).toContain('Old state');
|
|
33
|
+
const saved = await memory.toJSON();
|
|
34
|
+
memory.configure({ promptCaching: false });
|
|
35
|
+
expect(await text(memory)).not.toContain('Old state');
|
|
36
|
+
expect((await memory.render()).every(message => !message.cacheControl)).toBe(true);
|
|
37
|
+
expect(await memory.toJSON()).toEqual(saved);
|
|
38
|
+
memory.configure({ promptCaching: true, instructions: 'Current constraint' });
|
|
39
|
+
expect((await memory.render()).some(message => message.cacheControl)).toBe(true);
|
|
40
|
+
memory.configure({ instructions: undefined, promptCaching: undefined });
|
|
41
|
+
expect(memory.instructions).toBe('Current constraint');
|
|
42
|
+
expect((await memory.render()).some(message => message.cacheControl)).toBe(true);
|
|
43
|
+
expect((await memory.toJSON()).observations).toEqual(saved.observations);
|
|
44
|
+
});
|
|
45
|
+
for (const instructions of ['Current constraint', '', null, 'Original constraint', undefined]) {
|
|
46
|
+
test(`same-policy instruction update invalidates frozen retention only when changed: ${JSON.stringify(instructions)}`, async () => {
|
|
47
|
+
const memory = new AgentMemory({ instructions: 'Original constraint', promptCaching: true });
|
|
48
|
+
memory.recordObservation(Observation.fromConnector('fixture', 'Old state', { type: 'state', limit: 1 }));
|
|
49
|
+
await memory.render();
|
|
50
|
+
await memory.render();
|
|
51
|
+
memory.remember({ key: 'saved', text: 'Retained fact', sources: [0] });
|
|
52
|
+
memory.recordObservation(Observation.fromConnector('fixture', 'New state', { type: 'state', limit: 1 }));
|
|
53
|
+
const saved = await memory.toJSON();
|
|
54
|
+
memory.configure({ instructions, promptCaching: true });
|
|
55
|
+
const changed = instructions !== undefined && instructions !== 'Original constraint';
|
|
56
|
+
const rendered = await text(memory);
|
|
57
|
+
expect(rendered.includes('Old state')).toBe(!changed);
|
|
58
|
+
expect(rendered).toContain('New state');
|
|
59
|
+
expect(rendered).toContain('Retained fact');
|
|
60
|
+
expect(memory.instructions).toBe(instructions === undefined ? 'Original constraint' : instructions);
|
|
61
|
+
const after = await memory.toJSON();
|
|
62
|
+
expect(after.observations).toEqual(saved.observations);
|
|
63
|
+
expect(after.notes).toEqual(saved.notes);
|
|
64
|
+
// Changed instructions reset the cache rotation; unchanged instructions preserve its cadence.
|
|
65
|
+
memory.recordObservation(Observation.fromConnector('fixture', 'Newest state', { type: 'state', limit: 1 }));
|
|
66
|
+
const next = await memory.render();
|
|
67
|
+
expect(JSON.stringify(next).includes('New state')).toBe(changed);
|
|
68
|
+
expect(JSON.stringify(next)).toContain('Newest state');
|
|
69
|
+
expect(next.some(message => message.cacheControl)).toBe(true);
|
|
70
|
+
});
|
|
71
|
+
}
|
|
12
72
|
for (const promptCaching of [false, true])
|
|
13
73
|
test(`current observations replace cached state without changing history or audit, caching ${promptCaching}`, async () => {
|
|
14
74
|
const memory = new AgentMemory({ promptCaching });
|
|
@@ -142,11 +202,14 @@ test('loading legacy memory clears notes and visibility/cache state', async () =
|
|
|
142
202
|
expect(await text(memory)).toContain('New task observation');
|
|
143
203
|
});
|
|
144
204
|
test('malformed checkpoint provenance does not replace current memory', async () => {
|
|
145
|
-
const memory = new AgentMemory();
|
|
205
|
+
const memory = new AgentMemory({ instructions: 'Keep these instructions', promptCaching: true });
|
|
146
206
|
memory.recordObservation(screen(0));
|
|
147
207
|
const saved = await memory.toJSON();
|
|
148
|
-
await expect(memory.loadJSON({ ...saved,
|
|
208
|
+
await expect(memory.loadJSON({ ...saved, instructions: 42 })).rejects.toThrow('instructions must be a string');
|
|
209
|
+
expect(await memory.toJSON()).toEqual(saved);
|
|
210
|
+
await expect(memory.loadJSON({ ...saved, instructions: 'Rejected instructions', notes: [{ key: 'bad', text: 'claim', sources: [50] }] })).rejects.toThrow();
|
|
149
211
|
expect(await memory.toJSON()).toEqual(saved);
|
|
212
|
+
expect((await memory.render()).some(message => message.cacheControl)).toBe(true);
|
|
150
213
|
});
|
|
151
214
|
for (const promptCaching of [false, true])
|
|
152
215
|
test(`completed checks outlive the default thought window and can be corrected, caching ${promptCaching}`, async () => {
|