@oharness/engine 0.11.0 → 0.14.0
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/agent.d.ts +3 -0
- package/dist/agent/agent.js +32 -12
- package/dist/agent/agent.js.map +1 -1
- package/dist/agent/background-usage.d.ts +34 -0
- package/dist/agent/background-usage.js +53 -0
- package/dist/agent/background-usage.js.map +1 -0
- package/dist/agent/background.d.ts +2 -0
- package/dist/agent/background.js +11 -0
- package/dist/agent/background.js.map +1 -1
- package/dist/agent/memory-backup.d.ts +1 -1
- package/dist/agent/memory-backup.js +1 -1
- package/dist/agent/memory-backup.js.map +1 -1
- package/dist/agent/memory-digest.d.ts +107 -0
- package/dist/agent/memory-digest.js +208 -0
- package/dist/agent/memory-digest.js.map +1 -0
- package/dist/agent/memory-dream.d.ts +106 -0
- package/dist/agent/memory-dream.js +237 -0
- package/dist/agent/memory-dream.js.map +1 -0
- package/dist/agent/memory-ledger.d.ts +154 -0
- package/dist/agent/memory-ledger.js +179 -0
- package/dist/agent/memory-ledger.js.map +1 -0
- package/dist/agent/memory-provider.d.ts +12 -0
- package/dist/agent/memory-recall.d.ts +2 -0
- package/dist/agent/memory-recall.js +5 -2
- package/dist/agent/memory-recall.js.map +1 -1
- package/dist/agent/memory-usage.d.ts +20 -0
- package/dist/agent/memory-usage.js +16 -1
- package/dist/agent/memory-usage.js.map +1 -1
- package/dist/agent/memory.d.ts +3 -0
- package/dist/agent/memory.js +24 -4
- package/dist/agent/memory.js.map +1 -1
- package/dist/agent/skills.d.ts +16 -3
- package/dist/agent/skills.js +36 -2
- package/dist/agent/skills.js.map +1 -1
- package/dist/agent/store.d.ts +7 -0
- package/dist/agent/store.js +14 -1
- package/dist/agent/store.js.map +1 -1
- package/dist/agent/subagent.d.ts +15 -2
- package/dist/agent/subagent.js +70 -16
- package/dist/agent/subagent.js.map +1 -1
- package/dist/config-files.d.ts +6 -1
- package/dist/config-files.js +7 -2
- package/dist/config-files.js.map +1 -1
- package/dist/config.d.ts +9 -0
- package/dist/config.js +2 -0
- package/dist/config.js.map +1 -1
- package/dist/foreign.d.ts +81 -0
- package/dist/foreign.js +198 -0
- package/dist/foreign.js.map +1 -0
- package/dist/harness.d.ts +47 -0
- package/dist/harness.js +186 -13
- package/dist/harness.js.map +1 -1
- package/dist/mcp/registry.d.ts +2 -0
- package/dist/mcp/registry.js +1 -0
- package/dist/mcp/registry.js.map +1 -1
- package/dist/mcp/registry.json +394 -1
- package/dist/paths.d.ts +19 -0
- package/dist/paths.js +38 -0
- package/dist/paths.js.map +1 -1
- package/dist/plugin-market.d.ts +57 -0
- package/dist/plugin-market.js +229 -0
- package/dist/plugin-market.js.map +1 -0
- package/dist/plugin-market.json +1246 -0
- package/dist/plugins.d.ts +49 -2
- package/dist/plugins.js +252 -10
- package/dist/plugins.js.map +1 -1
- package/dist/runtime-plugins/builtin/dreaming-coordinator.d.ts +244 -0
- package/dist/runtime-plugins/builtin/dreaming-coordinator.js +548 -0
- package/dist/runtime-plugins/builtin/dreaming-coordinator.js.map +1 -0
- package/dist/runtime-plugins/builtin/dreaming.d.ts +30 -0
- package/dist/runtime-plugins/builtin/dreaming.js +231 -0
- package/dist/runtime-plugins/builtin/dreaming.js.map +1 -0
- package/dist/runtime-plugins/builtin/memory.d.ts +89 -2
- package/dist/runtime-plugins/builtin/memory.js +186 -5
- package/dist/runtime-plugins/builtin/memory.js.map +1 -1
- package/dist/runtime-plugins/host.js +2 -0
- package/dist/runtime-plugins/host.js.map +1 -1
- package/dist/util/toml.d.ts +22 -0
- package/dist/util/toml.js +305 -0
- package/dist/util/toml.js.map +1 -0
- package/package.json +2 -2
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
import { noteHash, memoryNote } from "../../agent/memory.js";
|
|
2
|
+
import { consolidationEnabled } from "../../agent/memory-limits.js";
|
|
3
|
+
import { DEFAULT_DREAMING, DreamCoordinator } from "./dreaming-coordinator.js";
|
|
4
|
+
import { BackgroundUsage } from "../../agent/background-usage.js";
|
|
5
|
+
import { localDay } from "../../agent/memory-ledger.js";
|
|
6
|
+
import { usageFor } from "./memory.js";
|
|
7
|
+
import { updateRuntimePluginConfig } from "../../config-files.js";
|
|
8
|
+
const SETTING_RANGES = {
|
|
9
|
+
idleHours: [0, 24 * 30], minUserTurns: [1, 1_000], startDelayMinutes: [0, 24 * 60],
|
|
10
|
+
minClaims: [1, 1_000], maxWaitHours: [1, 24 * 365], maxClaims: [1, 200], dailyTokens: [0, 10_000_000],
|
|
11
|
+
};
|
|
12
|
+
export function parseDreamingConfig(raw) {
|
|
13
|
+
if (raw === undefined || raw === null)
|
|
14
|
+
return { ...DEFAULT_DREAMING };
|
|
15
|
+
if (typeof raw !== "object" || Array.isArray(raw))
|
|
16
|
+
throw new Error("Invalid dreaming config");
|
|
17
|
+
const value = raw;
|
|
18
|
+
const settings = { ...DEFAULT_DREAMING };
|
|
19
|
+
if (value.auto !== undefined) {
|
|
20
|
+
if (value.auto !== true && value.auto !== false && value.auto !== "subscribers")
|
|
21
|
+
throw new Error("dreaming.auto must be true, false or \"subscribers\"");
|
|
22
|
+
settings.auto = value.auto;
|
|
23
|
+
}
|
|
24
|
+
for (const [key, [min, max]] of Object.entries(SETTING_RANGES)) {
|
|
25
|
+
const field = value[key];
|
|
26
|
+
if (field === undefined)
|
|
27
|
+
continue;
|
|
28
|
+
if (typeof field !== "number" || !Number.isFinite(field) || field < min || field > max)
|
|
29
|
+
throw new Error(`dreaming.${key} must be a number from ${min} to ${max}`);
|
|
30
|
+
settings[key] = field;
|
|
31
|
+
}
|
|
32
|
+
return settings;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* What the page shows for a change: its kind, the notes before, the notes as
|
|
36
|
+
* the change left them (when they still are), and its evidence.
|
|
37
|
+
*/
|
|
38
|
+
function changeView(change, notes) {
|
|
39
|
+
return {
|
|
40
|
+
id: change.id, op: change.op, summary: change.summary, evidence: change.evidence,
|
|
41
|
+
before: change.before.map((one) => one.note ? { id: one.id, summary: one.note.summary, body: one.note.body } : { id: one.id }),
|
|
42
|
+
after: change.after.map((one) => {
|
|
43
|
+
if (!one.hash)
|
|
44
|
+
return { id: one.id, removed: true };
|
|
45
|
+
const now = notes.get(one.id);
|
|
46
|
+
return now && now.hash === one.hash ? { id: one.id, summary: now.summary, body: now.body } : { id: one.id, editedSince: true };
|
|
47
|
+
}),
|
|
48
|
+
...(change.run ? { run: change.run } : {}),
|
|
49
|
+
...(change.fromProposal ? { fromProposal: true } : {}),
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
function proposalView(op, notes) {
|
|
53
|
+
const ids = op.op === "merge" ? op.ids : "id" in op ? [op.id] : [];
|
|
54
|
+
return {
|
|
55
|
+
op: op.op,
|
|
56
|
+
targets: ids.map((id) => notes.get(id) ?? { id, summary: id, body: "" }),
|
|
57
|
+
...("summary" in op ? { summary: op.summary, body: op.body, tags: op.tags } : {}),
|
|
58
|
+
...("reason" in op ? { reason: op.reason } : {}),
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
function runView(run) {
|
|
62
|
+
return {
|
|
63
|
+
at: run.at, digested: run.digested, ...(run.budgetStopped ? { budgetStopped: true } : {}),
|
|
64
|
+
applied: run.scopes.reduce((sum, scope) => sum + scope.applied.length, 0),
|
|
65
|
+
proposals: run.scopes.reduce((sum, scope) => sum + scope.proposals, 0),
|
|
66
|
+
errors: run.scopes.flatMap((scope) => scope.error ? [scope.error] : []),
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
/** This month's dreaming, across every project: what the usage page also counts. */
|
|
70
|
+
async function monthSpend(now = Date.now()) {
|
|
71
|
+
const month = localDay(now).slice(0, 7);
|
|
72
|
+
const records = (await BackgroundUsage.shared.records("dreaming")).filter((record) => record.day.startsWith(month));
|
|
73
|
+
return {
|
|
74
|
+
month,
|
|
75
|
+
calls: records.reduce((sum, r) => sum + r.calls, 0),
|
|
76
|
+
tokens: records.reduce((sum, r) => sum + r.input + r.output + r.cacheRead + r.cacheWrite, 0),
|
|
77
|
+
costUsd: records.reduce((sum, r) => sum + r.costUsd, 0),
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Whether dreaming runs on its own, as last chosen on a page in this process.
|
|
82
|
+
*
|
|
83
|
+
* The choice is the user's, not a project's, and every harness in the process
|
|
84
|
+
* has a dreaming plugin with the settings it booted with; this is what keeps
|
|
85
|
+
* them agreeing after someone flips the switch, until they next boot from the
|
|
86
|
+
* file the choice was written to.
|
|
87
|
+
*/
|
|
88
|
+
let chosenAuto;
|
|
89
|
+
/** Set the choice, for every project open in this process, and keep it. */
|
|
90
|
+
export async function chooseAuto(auto, persist = true) {
|
|
91
|
+
chosenAuto = auto;
|
|
92
|
+
if (auto === undefined)
|
|
93
|
+
return;
|
|
94
|
+
// A new entry says it is not required: an app built before dreaming existed
|
|
95
|
+
// reads the same file, and would otherwise refuse to start without it.
|
|
96
|
+
if (persist)
|
|
97
|
+
await updateRuntimePluginConfig("dreaming", (config) => ({ ...config, auto }), { required: false });
|
|
98
|
+
}
|
|
99
|
+
export function createDreamingPlugin(options) {
|
|
100
|
+
return {
|
|
101
|
+
id: "dreaming", version: "1.0.0", apiVersion: 1, title: "Dreaming",
|
|
102
|
+
description: "Reads quiet conversations and keeps your notes current: corrections, patterns across conversations, merges. Changes can be undone.",
|
|
103
|
+
parseConfig: parseDreamingConfig,
|
|
104
|
+
setup(ctx, booted) {
|
|
105
|
+
const coordinator = options.coordinator ?? DreamCoordinator.shared;
|
|
106
|
+
// Read on every use: the switch on the page changes it while running.
|
|
107
|
+
const current = () => chosenAuto === undefined ? booted : { ...booted, auto: chosenAuto };
|
|
108
|
+
const services = options.memory();
|
|
109
|
+
const bridge = options.bridge;
|
|
110
|
+
if (!services.project || !options.generate || !bridge) {
|
|
111
|
+
// Nothing to build on: no notes to dream over, or no way to write them.
|
|
112
|
+
ctx.logger.info(services.project ? "dreaming idle: no model or no memory bridge" : "dreaming idle: memory is off");
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
const generate = options.generate;
|
|
116
|
+
const project = services.project, user = services.user;
|
|
117
|
+
const notes = async (scope) => {
|
|
118
|
+
const service = scope === "user" ? user : project;
|
|
119
|
+
if (!service)
|
|
120
|
+
return undefined;
|
|
121
|
+
const usage = usageFor(scope === "user" ? "user" : `project:${options.projectId}`);
|
|
122
|
+
return (await service.store.load()).map((entry) => {
|
|
123
|
+
const note = memoryNote(entry);
|
|
124
|
+
const recalled = usage.get(note.id);
|
|
125
|
+
return {
|
|
126
|
+
id: note.id, summary: note.summary, body: note.body, tags: note.tags, createdAt: note.createdAt, scope,
|
|
127
|
+
...(note.type ? { type: note.type } : {}), ...(note.updatedAt ? { updatedAt: note.updatedAt } : {}),
|
|
128
|
+
hash: noteHash(note), ...(recalled ? { recalled: { count: recalled.count, last: recalled.last } } : {}),
|
|
129
|
+
};
|
|
130
|
+
});
|
|
131
|
+
};
|
|
132
|
+
const maintenance = () => {
|
|
133
|
+
if (!bridge.maintenance)
|
|
134
|
+
throw new Error("Memory is not running");
|
|
135
|
+
return bridge.maintenance;
|
|
136
|
+
};
|
|
137
|
+
const target = {
|
|
138
|
+
projectId: options.projectId,
|
|
139
|
+
sessions: options.sessions,
|
|
140
|
+
notes,
|
|
141
|
+
generate: (system, prompt, signal) => generate(system, prompt, signal),
|
|
142
|
+
settings: current,
|
|
143
|
+
autoOn: () => consolidationEnabled(current().auto, [project.tier, user?.tier]),
|
|
144
|
+
commit: (scope, changes) => maintenance().commit(scope, changes),
|
|
145
|
+
announce: (summaries) => { bridge.maintenance?.announce(summaries); },
|
|
146
|
+
};
|
|
147
|
+
// Background work waits while the person is working.
|
|
148
|
+
ctx.hooks.on("UserPromptSubmit", () => { coordinator.activity(); });
|
|
149
|
+
ctx.hooks.on("Stop", () => { coordinator.activity(); });
|
|
150
|
+
let error;
|
|
151
|
+
let controller;
|
|
152
|
+
const scopeOf = (value) => value === "user" && user ? "user" : "project";
|
|
153
|
+
const unregisterSection = bridge.sections.register({
|
|
154
|
+
id: "dreaming",
|
|
155
|
+
async read() {
|
|
156
|
+
const [state, pending, projectNotes, userNotes, runs, spend, schedule] = await Promise.all([
|
|
157
|
+
coordinator.state(target), coordinator.pending(target), notes("project"), notes("user"),
|
|
158
|
+
coordinator.store.runs(options.projectId, 50), monthSpend(), coordinator.nextDream(target),
|
|
159
|
+
]);
|
|
160
|
+
const labels = new Map();
|
|
161
|
+
for (const note of [...(projectNotes ?? []), ...(userNotes ?? [])])
|
|
162
|
+
labels.set(note.id, { id: note.id, summary: note.summary, body: note.body, hash: note.hash });
|
|
163
|
+
const run = coordinator.lastRun(target);
|
|
164
|
+
return {
|
|
165
|
+
auto: target.autoOn(), autoSetting: current().auto, running: !!controller || coordinator.isRunning(), pending, spend, schedule,
|
|
166
|
+
settings: (({ minClaims, maxWaitHours, idleHours, dailyTokens }) => ({ minClaims, maxWaitHours, idleHours, dailyTokens }))(current()),
|
|
167
|
+
runs,
|
|
168
|
+
...(run ? { lastRun: runView(run) } : {}),
|
|
169
|
+
proposals: state.proposals.slice(0, 30).map((one) => ({
|
|
170
|
+
id: one.id, scope: one.scope, at: one.at, ...(one.why ? { why: one.why } : {}), evidence: one.evidence,
|
|
171
|
+
...proposalView(one.op, labels),
|
|
172
|
+
})).map(({ targets, ...rest }) => ({
|
|
173
|
+
...rest, targets: targets.map(({ hash: _hash, ...target }) => target),
|
|
174
|
+
})),
|
|
175
|
+
log: state.log.map((one) => ({ scope: one.scope, at: one.at, undone: one.undone, ...changeView(one.change, labels) })),
|
|
176
|
+
...(error ? { error } : {}),
|
|
177
|
+
};
|
|
178
|
+
},
|
|
179
|
+
async act(input) {
|
|
180
|
+
error = undefined;
|
|
181
|
+
if (input.action === "dreamNow") {
|
|
182
|
+
if (controller)
|
|
183
|
+
return;
|
|
184
|
+
// Started, not awaited: reading ten conversations and dreaming two
|
|
185
|
+
// scopes outlasts a page request. The page polls while `running`.
|
|
186
|
+
controller = new AbortController();
|
|
187
|
+
const signal = AbortSignal.any([ctx.signal, controller.signal]);
|
|
188
|
+
void coordinator.dreamNow(target, signal)
|
|
189
|
+
.catch((failure) => {
|
|
190
|
+
error = failure instanceof Error ? failure.message : String(failure);
|
|
191
|
+
ctx.logger.warn("dream now failed", { error });
|
|
192
|
+
})
|
|
193
|
+
.finally(() => { controller = undefined; });
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
if (input.action === "setAuto") {
|
|
197
|
+
if (typeof input.auto !== "boolean")
|
|
198
|
+
throw new Error("auto must be true or false");
|
|
199
|
+
await chooseAuto(input.auto);
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
const id = typeof input.id === "string" ? input.id : "";
|
|
203
|
+
const scope = scopeOf(input.scope);
|
|
204
|
+
try {
|
|
205
|
+
if (input.action === "applyProposal")
|
|
206
|
+
await coordinator.applyProposal(target, scope, id);
|
|
207
|
+
else if (input.action === "dismissProposal")
|
|
208
|
+
await coordinator.dismissProposal(target, scope, id);
|
|
209
|
+
else if (input.action === "undo")
|
|
210
|
+
await coordinator.undo(target, scope, id);
|
|
211
|
+
else
|
|
212
|
+
throw new Error("Unknown dreaming action");
|
|
213
|
+
}
|
|
214
|
+
catch (failure) {
|
|
215
|
+
// Shown in the section, not as a page failure: the page itself is fine.
|
|
216
|
+
error = failure instanceof Error ? failure.message : String(failure);
|
|
217
|
+
}
|
|
218
|
+
},
|
|
219
|
+
});
|
|
220
|
+
ctx.onDispose(unregisterSection);
|
|
221
|
+
ctx.onStart(() => {
|
|
222
|
+
const unregister = coordinator.register(target, ctx.logger);
|
|
223
|
+
// While automatic passes are on, memory's own end-of-run extraction
|
|
224
|
+
// and background merging stand down: one maintainer, not two.
|
|
225
|
+
const release = bridge.maintenance?.claim("dreaming", () => target.autoOn());
|
|
226
|
+
ctx.onDispose(() => { controller?.abort(); unregister(); release?.(); });
|
|
227
|
+
});
|
|
228
|
+
},
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
//# sourceMappingURL=dreaming.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dreaming.js","sourceRoot":"","sources":["../../../src/runtime-plugins/builtin/dreaming.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { MemoryStore } from "../../agent/memory.ts";
|
|
2
2
|
import type { MemoryNote, MemoryStores } from "../../agent/memory.ts";
|
|
3
|
-
import type { MemoryPolicy, MemoryScope } from "../../agent/memory-limits.ts";
|
|
3
|
+
import type { MemoryPolicy, MemoryScope, MemoryType } from "../../agent/memory-limits.ts";
|
|
4
|
+
import { MemoryUsage } from "../../agent/memory-usage.ts";
|
|
4
5
|
import type { BackupReason } from "../../agent/memory-backup.ts";
|
|
5
6
|
import type { MemoryFailureInfo } from "../../agent/memory-errors.ts";
|
|
6
7
|
import type { MergeField } from "../../agent/memory-merge.ts";
|
|
7
|
-
import type { ExtractedNote, ExtractionResult } from "../../agent/memory-provider.ts";
|
|
8
|
+
import type { ExtractedNote, ExtractedSummary, ExtractionResult } from "../../agent/memory-provider.ts";
|
|
8
9
|
import type { RuntimePlugin } from "../types.ts";
|
|
9
10
|
import { MemorySyncCoordinator } from "./memory-coordinator.ts";
|
|
10
11
|
type Identity = {
|
|
@@ -62,6 +63,91 @@ export interface MemoryPluginOptions extends Omit<Options, "scope"> {
|
|
|
62
63
|
userDirectory?: string;
|
|
63
64
|
/** One reply from the small model, for merge proposals. Without it merges are drafted by hand. */
|
|
64
65
|
generate?(system: string, prompt: string, signal?: AbortSignal): Promise<string>;
|
|
66
|
+
/** Where plugins that build on memory, such as dreaming, reach it. */
|
|
67
|
+
bridge?: MemoryBridge;
|
|
68
|
+
}
|
|
69
|
+
/** A note's content as a dream proposes it. */
|
|
70
|
+
export interface DreamDraft {
|
|
71
|
+
summary: string;
|
|
72
|
+
body: string;
|
|
73
|
+
tags: string[];
|
|
74
|
+
type?: MemoryType;
|
|
75
|
+
}
|
|
76
|
+
/** One change a dream commits; `restore` puts back what a change replaced (undo). */
|
|
77
|
+
export type DreamChange = {
|
|
78
|
+
kind: "create";
|
|
79
|
+
draft: DreamDraft;
|
|
80
|
+
} | {
|
|
81
|
+
kind: "update";
|
|
82
|
+
id: string;
|
|
83
|
+
expectedHash: string;
|
|
84
|
+
draft: DreamDraft;
|
|
85
|
+
} | {
|
|
86
|
+
kind: "merge";
|
|
87
|
+
ids: string[];
|
|
88
|
+
expectedHashes: string[];
|
|
89
|
+
draft: DreamDraft;
|
|
90
|
+
} | {
|
|
91
|
+
kind: "restore";
|
|
92
|
+
id: string;
|
|
93
|
+
expectedHash: string | null;
|
|
94
|
+
note: MemoryNote | null;
|
|
95
|
+
};
|
|
96
|
+
/** What a committed change did: each note it touched, before and after. */
|
|
97
|
+
export interface DreamCommitted {
|
|
98
|
+
ids: string[];
|
|
99
|
+
before: Array<{
|
|
100
|
+
id: string;
|
|
101
|
+
note: MemoryNote | null;
|
|
102
|
+
}>;
|
|
103
|
+
after: Array<{
|
|
104
|
+
id: string;
|
|
105
|
+
hash: string | null;
|
|
106
|
+
}>;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* What memory lets a maintenance plugin do. Every write goes through memory's
|
|
110
|
+
* own path: expected hashes, backups, and with sync on, the cloud first.
|
|
111
|
+
*/
|
|
112
|
+
export interface MemoryMaintenance {
|
|
113
|
+
/**
|
|
114
|
+
* Take over background upkeep while `active()` holds: memory's own
|
|
115
|
+
* end-of-run extraction and background merging stand down. Returns the
|
|
116
|
+
* release, which restores them.
|
|
117
|
+
*/
|
|
118
|
+
claim(owner: string, active: () => boolean): () => void;
|
|
119
|
+
/** All changes in one scope, as one batch; nothing is written if any is stale. */
|
|
120
|
+
commit(scope: MemoryScope, changes: readonly DreamChange[]): Promise<DreamCommitted[]>;
|
|
121
|
+
/** Told at the start of the next run in this project. */
|
|
122
|
+
announce(notes: ExtractedSummary[]): void;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* What the harness hands both memory and a plugin built on it. Plugins cannot
|
|
126
|
+
* reach each other; memory fills `maintenance` while it runs and reads
|
|
127
|
+
* `sections`, the other plugin does the reverse.
|
|
128
|
+
*/
|
|
129
|
+
export declare class MemoryBridge {
|
|
130
|
+
readonly sections: MemoryPageSections;
|
|
131
|
+
maintenance?: MemoryMaintenance;
|
|
132
|
+
}
|
|
133
|
+
/** A part of the Memory page another plugin supplies, rendered by the host. */
|
|
134
|
+
export interface MemoryPageSection {
|
|
135
|
+
id: string;
|
|
136
|
+
read(): Promise<unknown>;
|
|
137
|
+
act(input: Record<string, unknown>): Promise<void>;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Where plugins that build on memory, such as dreaming, put what they show on
|
|
141
|
+
* the Memory page. Plugins cannot reach each other, so the harness hands one
|
|
142
|
+
* of these to both: memory reads it, the other plugin fills it. Empty, the
|
|
143
|
+
* page is exactly what it was.
|
|
144
|
+
*/
|
|
145
|
+
export declare class MemoryPageSections {
|
|
146
|
+
private readonly sections;
|
|
147
|
+
register(section: MemoryPageSection): () => void;
|
|
148
|
+
/** Every section's state; one that fails is left out rather than failing the page. */
|
|
149
|
+
read(): Promise<Record<string, unknown>>;
|
|
150
|
+
act(id: string, input: Record<string, unknown>): Promise<void>;
|
|
65
151
|
}
|
|
66
152
|
type Conflict = {
|
|
67
153
|
id: string;
|
|
@@ -202,6 +288,7 @@ export declare function acquireMemoryService(options: Options): {
|
|
|
202
288
|
service: MemoryService;
|
|
203
289
|
release(): Promise<void>;
|
|
204
290
|
};
|
|
291
|
+
export declare function usageFor(stateKey: string): MemoryUsage;
|
|
205
292
|
/**
|
|
206
293
|
* Write what an extraction pass proposed. New notes first, so every
|
|
207
294
|
* `[[new:N]]` placeholder has an id to become; then the notes that used a
|
|
@@ -4,7 +4,7 @@ import { createHash } from "node:crypto";
|
|
|
4
4
|
import { AUTO_KINDS, reviewNotes } from "../../agent/memory-review.js";
|
|
5
5
|
import { CONSOLIDATE_PROMPT, draftByHand, mergePrompt, parseMergeReply, planMerge } from "../../agent/memory-consolidate.js";
|
|
6
6
|
import { rm } from "node:fs/promises";
|
|
7
|
-
import { MemoryStore, loadScoped, memoryIndexSection, memoryNote, noteHash, recallFromStores, storeFor, validateNote, validMemoryId } from "../../agent/memory.js";
|
|
7
|
+
import { MemoryStore, loadScoped, memoryIndexSection, memoryNote, newNoteId, noteHash, recallFromStores, storeFor, validateNote, validMemoryId } from "../../agent/memory.js";
|
|
8
8
|
import { DEFAULT_MEMORY_POLICY, MEMORY_SAFETY, consolidationEnabled, isMemoryType } from "../../agent/memory-limits.js";
|
|
9
9
|
import { rankNotes } from "../../agent/memory-recall.js";
|
|
10
10
|
import { findSecrets } from "../../agent/memory-redact.js";
|
|
@@ -20,6 +20,48 @@ import { readPluginFile, readUserFile, updateRuntimePluginConfig, writeUserFile
|
|
|
20
20
|
import { projectId } from "../../storage/project.js";
|
|
21
21
|
import { resolveSyncUrl } from "../../sync/settings.js";
|
|
22
22
|
import { MemorySyncCoordinator } from "./memory-coordinator.js";
|
|
23
|
+
/**
|
|
24
|
+
* What the harness hands both memory and a plugin built on it. Plugins cannot
|
|
25
|
+
* reach each other; memory fills `maintenance` while it runs and reads
|
|
26
|
+
* `sections`, the other plugin does the reverse.
|
|
27
|
+
*/
|
|
28
|
+
export class MemoryBridge {
|
|
29
|
+
sections = new MemoryPageSections();
|
|
30
|
+
maintenance;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Where plugins that build on memory, such as dreaming, put what they show on
|
|
34
|
+
* the Memory page. Plugins cannot reach each other, so the harness hands one
|
|
35
|
+
* of these to both: memory reads it, the other plugin fills it. Empty, the
|
|
36
|
+
* page is exactly what it was.
|
|
37
|
+
*/
|
|
38
|
+
export class MemoryPageSections {
|
|
39
|
+
sections = new Map();
|
|
40
|
+
register(section) {
|
|
41
|
+
if (!/^[a-z][a-z0-9-]{0,63}$/.test(section.id) || this.sections.has(section.id))
|
|
42
|
+
throw new Error("Invalid or duplicate memory page section");
|
|
43
|
+
this.sections.set(section.id, section);
|
|
44
|
+
return () => { if (this.sections.get(section.id) === section)
|
|
45
|
+
this.sections.delete(section.id); };
|
|
46
|
+
}
|
|
47
|
+
/** Every section's state; one that fails is left out rather than failing the page. */
|
|
48
|
+
async read() {
|
|
49
|
+
const out = {};
|
|
50
|
+
await Promise.all([...this.sections.values()].map(async (section) => {
|
|
51
|
+
try {
|
|
52
|
+
out[section.id] = await section.read();
|
|
53
|
+
}
|
|
54
|
+
catch { /* left out */ }
|
|
55
|
+
}));
|
|
56
|
+
return out;
|
|
57
|
+
}
|
|
58
|
+
async act(id, input) {
|
|
59
|
+
const section = this.sections.get(id);
|
|
60
|
+
if (!section)
|
|
61
|
+
throw new MemoryError("unknownAction");
|
|
62
|
+
await section.act(input);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
23
65
|
const digest = (x) => createHash("sha256").update(x).digest("hex");
|
|
24
66
|
const object = (x) => !!x && typeof x === "object" && !Array.isArray(x);
|
|
25
67
|
const HASH = /^[a-f0-9]{64}$/;
|
|
@@ -859,7 +901,7 @@ export function acquireMemoryService(options) {
|
|
|
859
901
|
}
|
|
860
902
|
/** Recall counts per logical scope, shared so one Home-side file has one writer. */
|
|
861
903
|
const sharedUsage = new Map();
|
|
862
|
-
function usageFor(stateKey) {
|
|
904
|
+
export function usageFor(stateKey) {
|
|
863
905
|
const key = stateKey;
|
|
864
906
|
let usage = sharedUsage.get(key);
|
|
865
907
|
if (!usage) {
|
|
@@ -949,7 +991,10 @@ export function createMemoryPlugin(options, onReady) {
|
|
|
949
991
|
ctx.tools.register(tool);
|
|
950
992
|
ctx.prompts.register(async () => {
|
|
951
993
|
try {
|
|
952
|
-
return memoryIndexSection((await loadScoped(stores)).notes, policy.indexChars, {
|
|
994
|
+
return memoryIndexSection((await loadScoped(stores)).notes, policy.indexChars, {
|
|
995
|
+
showScope: !!user,
|
|
996
|
+
recency: (note) => (usage[note.scope] ?? usage.project).recency(note, noteHash(memoryNote(note.entry))),
|
|
997
|
+
});
|
|
953
998
|
}
|
|
954
999
|
catch {
|
|
955
1000
|
ctx.logger.warn("Memory prompt unavailable; check local note files");
|
|
@@ -958,14 +1003,26 @@ export function createMemoryPlugin(options, onReady) {
|
|
|
958
1003
|
});
|
|
959
1004
|
const inUse = () => { for (const one of services)
|
|
960
1005
|
one.activity(); };
|
|
1006
|
+
const maintainers = new Set();
|
|
1007
|
+
const maintained = () => [...maintainers].some((active) => { try {
|
|
1008
|
+
return active();
|
|
1009
|
+
}
|
|
1010
|
+
catch {
|
|
1011
|
+
return false;
|
|
1012
|
+
} });
|
|
1013
|
+
const notices = [];
|
|
961
1014
|
const provider = {
|
|
962
1015
|
recall: (input) => {
|
|
963
1016
|
// A message is being sent in this project: its notes should be current.
|
|
964
1017
|
inUse();
|
|
965
1018
|
return recallFromStores(stores, input, policy.recall, usage);
|
|
966
1019
|
},
|
|
1020
|
+
// What dreaming changed while nobody was looking, told at the next run.
|
|
1021
|
+
notices: async () => notices.splice(0),
|
|
967
1022
|
extraction: {
|
|
968
|
-
|
|
1023
|
+
// A plugin maintaining these notes in the background reads the
|
|
1024
|
+
// conversation itself; two writers would each act on it.
|
|
1025
|
+
policy: () => maintained() ? { ...policy.autoExtract, onStop: false, onCompaction: false } : policy.autoExtract,
|
|
969
1026
|
async candidates(text, limit) {
|
|
970
1027
|
const { notes } = await loadScoped(stores);
|
|
971
1028
|
const chosen = new Map(rankNotes(notes, text.slice(-8_000)).slice(0, limit).map((entry) => [entry.note.id, entry.note]));
|
|
@@ -1074,11 +1131,130 @@ export function createMemoryPlugin(options, onReady) {
|
|
|
1074
1131
|
await store.apply(edit.id, edit.note, edit.expectedHash, "local");
|
|
1075
1132
|
}
|
|
1076
1133
|
};
|
|
1134
|
+
/**
|
|
1135
|
+
* Commit a dream's changes to one scope as one batch, by the merge path:
|
|
1136
|
+
* with sync on, synced first and committed to the cloud before this
|
|
1137
|
+
* machine, so an edit from another device in between refuses the whole
|
|
1138
|
+
* batch; otherwise local edits, each against the hash it was planned
|
|
1139
|
+
* from and backed up first. Nothing is written if any change is stale.
|
|
1140
|
+
*/
|
|
1141
|
+
const commitDream = async (scope, changes) => {
|
|
1142
|
+
if (scope === "user" && !user)
|
|
1143
|
+
throw new MemoryError("unknownAction");
|
|
1144
|
+
const service = serviceFor(scope);
|
|
1145
|
+
const cloudFirst = await service.syncReady();
|
|
1146
|
+
if (cloudFirst)
|
|
1147
|
+
await service.sync();
|
|
1148
|
+
const all = await allNotes();
|
|
1149
|
+
const strip = ({ scope: _scope, ...note }) => note;
|
|
1150
|
+
const inScope = new Map(all.filter((note) => note.scope === scope).map((note) => [note.id, note]));
|
|
1151
|
+
const now = Date.now();
|
|
1152
|
+
const edits = [];
|
|
1153
|
+
const touched = new Set();
|
|
1154
|
+
const add = (edit) => {
|
|
1155
|
+
const key = `${edit.scope}:${edit.id}`;
|
|
1156
|
+
if (touched.has(key))
|
|
1157
|
+
throw new MemoryError("invalidBatch");
|
|
1158
|
+
touched.add(key);
|
|
1159
|
+
edits.push(edit);
|
|
1160
|
+
};
|
|
1161
|
+
const current = (id, expected) => {
|
|
1162
|
+
const note = inScope.get(id);
|
|
1163
|
+
if (noteHash(note ? strip(note) : undefined) !== expected)
|
|
1164
|
+
throw new MemoryError("mergeStale");
|
|
1165
|
+
return note ? strip(note) : undefined;
|
|
1166
|
+
};
|
|
1167
|
+
const draftNote = (id, draft, createdAt) => ({
|
|
1168
|
+
id, summary: draft.summary, body: draft.body, tags: [...draft.tags], createdAt, updatedAt: now,
|
|
1169
|
+
...(draft.type ? { type: draft.type } : {}),
|
|
1170
|
+
});
|
|
1171
|
+
const results = [];
|
|
1172
|
+
for (const change of changes) {
|
|
1173
|
+
if (change.kind === "create") {
|
|
1174
|
+
const note = draftNote(newNoteId(change.draft.summary), change.draft, now);
|
|
1175
|
+
add({ scope, id: note.id, note, expectedHash: null });
|
|
1176
|
+
results.push({ ids: [note.id], before: [{ id: note.id, note: null }], after: [{ id: note.id, hash: noteHash(note) }] });
|
|
1177
|
+
}
|
|
1178
|
+
else if (change.kind === "update") {
|
|
1179
|
+
const old = current(change.id, change.expectedHash);
|
|
1180
|
+
if (!old)
|
|
1181
|
+
throw new MemoryError("mergeStale");
|
|
1182
|
+
const note = draftNote(old.id, { ...change.draft, ...(change.draft.type ?? old.type ? { type: change.draft.type ?? old.type } : {}) }, old.createdAt);
|
|
1183
|
+
add({ scope, id: old.id, note, expectedHash: change.expectedHash });
|
|
1184
|
+
results.push({ ids: [old.id], before: [{ id: old.id, note: old }], after: [{ id: old.id, hash: noteHash(note) }] });
|
|
1185
|
+
}
|
|
1186
|
+
else if (change.kind === "merge") {
|
|
1187
|
+
const group = change.ids.map((id, i) => current(id, change.expectedHashes[i] ?? null));
|
|
1188
|
+
if (group.some((note) => !note))
|
|
1189
|
+
throw new MemoryError("mergeGone");
|
|
1190
|
+
const planned = planMerge({ all, scope, ids: change.ids, draft: change.draft, hash: noteHash, now });
|
|
1191
|
+
for (const edit of planned)
|
|
1192
|
+
add(edit);
|
|
1193
|
+
results.push({
|
|
1194
|
+
ids: [...change.ids],
|
|
1195
|
+
before: group.map((note) => ({ id: note.id, note: note })),
|
|
1196
|
+
after: planned.filter((edit) => edit.scope === scope && change.ids.includes(edit.id)).map((edit) => ({ id: edit.id, hash: noteHash(edit.note) })),
|
|
1197
|
+
});
|
|
1198
|
+
}
|
|
1199
|
+
else {
|
|
1200
|
+
const old = current(change.id, change.expectedHash);
|
|
1201
|
+
add({ scope, id: change.id, ...(change.note ? { note: change.note } : {}), expectedHash: change.expectedHash });
|
|
1202
|
+
results.push({ ids: [change.id], before: [{ id: change.id, note: old ?? null }], after: [{ id: change.id, hash: noteHash(change.note ?? undefined) }] });
|
|
1203
|
+
}
|
|
1204
|
+
}
|
|
1205
|
+
for (const edit of edits) {
|
|
1206
|
+
if (!edit.note)
|
|
1207
|
+
continue;
|
|
1208
|
+
if (findSecrets(`${edit.note.summary}\n${edit.note.body}`).length)
|
|
1209
|
+
throw new MemoryError("mergedSecret");
|
|
1210
|
+
validateNote(edit.note, serviceFor(edit.scope).noteBytesLimit());
|
|
1211
|
+
}
|
|
1212
|
+
// How recent each rewritten note was, before the dream touched it.
|
|
1213
|
+
const recencies = new Map(edits.filter((edit) => edit.scope === scope && edit.note && inScope.has(edit.id)).map((edit) => {
|
|
1214
|
+
const old = strip(inScope.get(edit.id));
|
|
1215
|
+
return [edit.id, (usage[scope] ?? usage.project).recency(old, noteHash(old))];
|
|
1216
|
+
}));
|
|
1217
|
+
const own = edits.filter((edit) => edit.scope === scope);
|
|
1218
|
+
if (cloudFirst)
|
|
1219
|
+
await service.commitFirst(own.map(({ id, note, expectedHash }) => ({ id, expectedHash, ...(note ? { note } : {}) })), "dream");
|
|
1220
|
+
for (const edit of cloudFirst ? edits.filter((one) => one.scope !== scope) : edits) {
|
|
1221
|
+
const store = serviceFor(edit.scope).store;
|
|
1222
|
+
const live = (await store.load()).find((entry) => entry.id === edit.id);
|
|
1223
|
+
if (noteHash(live ? memoryNote(live) : undefined) !== edit.expectedHash)
|
|
1224
|
+
throw new MemoryError("mergeStale");
|
|
1225
|
+
if (live)
|
|
1226
|
+
await backupNote(store, memoryNote(live), "dream");
|
|
1227
|
+
await store.apply(edit.id, edit.note, edit.expectedHash, "local");
|
|
1228
|
+
}
|
|
1229
|
+
// A rewrite keeps the place in the index its use earned (an undo
|
|
1230
|
+
// restores the old content, whose hash no longer matches, so it does too).
|
|
1231
|
+
for (const result of results)
|
|
1232
|
+
for (const after of result.after) {
|
|
1233
|
+
const before = recencies.get(after.id);
|
|
1234
|
+
if (after.hash && before !== undefined && changes.some((change) => change.kind !== "restore"))
|
|
1235
|
+
(usage[scope] ?? usage.project).markDreamed(after.id, after.hash, before);
|
|
1236
|
+
}
|
|
1237
|
+
return results;
|
|
1238
|
+
};
|
|
1239
|
+
if (options.bridge) {
|
|
1240
|
+
const bridge = options.bridge;
|
|
1241
|
+
const maintenance = {
|
|
1242
|
+
claim(_owner, active) {
|
|
1243
|
+
maintainers.add(active);
|
|
1244
|
+
return () => { maintainers.delete(active); };
|
|
1245
|
+
},
|
|
1246
|
+
commit: commitDream,
|
|
1247
|
+
announce(notes) { notices.push(...notes); },
|
|
1248
|
+
};
|
|
1249
|
+
bridge.maintenance = maintenance;
|
|
1250
|
+
ctx.onDispose(() => { if (bridge.maintenance === maintenance)
|
|
1251
|
+
bridge.maintenance = undefined; });
|
|
1252
|
+
}
|
|
1077
1253
|
const autoOn = () => consolidationEnabled(policy.consolidate.auto, services.map((one) => one.tier));
|
|
1078
1254
|
/** The background pass: duplicates only, merged by the model, within a cooldown. */
|
|
1079
1255
|
const autoConsolidate = async () => {
|
|
1080
1256
|
const settings = policy.consolidate;
|
|
1081
|
-
if (!autoOn() || !options.generate || settings.maxMerges === 0 || lifetime.signal.aborted)
|
|
1257
|
+
if (!autoOn() || !options.generate || settings.maxMerges === 0 || lifetime.signal.aborted || maintained())
|
|
1082
1258
|
return;
|
|
1083
1259
|
const state = readReview();
|
|
1084
1260
|
if (Date.now() - (state.lastAuto ?? 0) < settings.cooldownHours * 3_600_000)
|
|
@@ -1120,7 +1296,9 @@ export function createMemoryPlugin(options, onReady) {
|
|
|
1120
1296
|
const backups = (await listBackups(50).catch(() => [])).filter((backup) => backup.scope === "user" || backup.projectId === options.projectId);
|
|
1121
1297
|
const findings = await review().catch(() => []);
|
|
1122
1298
|
const reviewState = readReview();
|
|
1299
|
+
const sections = options.bridge ? await options.bridge.sections.read() : {};
|
|
1123
1300
|
return {
|
|
1301
|
+
...(Object.keys(sections).length ? { sections } : {}),
|
|
1124
1302
|
review: findings.slice(0, 100),
|
|
1125
1303
|
reviewTotal: findings.length,
|
|
1126
1304
|
...(proposal ? { proposal } : {}),
|
|
@@ -1227,6 +1405,9 @@ export function createMemoryPlugin(options, onReady) {
|
|
|
1227
1405
|
else if (input.action === "cancelMerge") {
|
|
1228
1406
|
proposal = undefined;
|
|
1229
1407
|
}
|
|
1408
|
+
else if (input.action === "section" && typeof input.section === "string" && options.bridge) {
|
|
1409
|
+
await options.bridge.sections.act(input.section, object(input.input) ? input.input : {});
|
|
1410
|
+
}
|
|
1230
1411
|
else if (input.action === "merge" && proposal && object(input.note)) {
|
|
1231
1412
|
const note = input.note;
|
|
1232
1413
|
const draft = {
|