@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.
Files changed (81) hide show
  1. package/dist/agent/agent.d.ts +3 -0
  2. package/dist/agent/agent.js +32 -12
  3. package/dist/agent/agent.js.map +1 -1
  4. package/dist/agent/background-usage.d.ts +34 -0
  5. package/dist/agent/background-usage.js +53 -0
  6. package/dist/agent/background-usage.js.map +1 -0
  7. package/dist/agent/background.d.ts +2 -0
  8. package/dist/agent/background.js +11 -0
  9. package/dist/agent/background.js.map +1 -1
  10. package/dist/agent/memory-backup.d.ts +1 -1
  11. package/dist/agent/memory-backup.js +1 -1
  12. package/dist/agent/memory-backup.js.map +1 -1
  13. package/dist/agent/memory-digest.d.ts +107 -0
  14. package/dist/agent/memory-digest.js +208 -0
  15. package/dist/agent/memory-digest.js.map +1 -0
  16. package/dist/agent/memory-dream.d.ts +106 -0
  17. package/dist/agent/memory-dream.js +237 -0
  18. package/dist/agent/memory-dream.js.map +1 -0
  19. package/dist/agent/memory-ledger.d.ts +154 -0
  20. package/dist/agent/memory-ledger.js +179 -0
  21. package/dist/agent/memory-ledger.js.map +1 -0
  22. package/dist/agent/memory-provider.d.ts +12 -0
  23. package/dist/agent/memory-recall.d.ts +2 -0
  24. package/dist/agent/memory-recall.js +5 -2
  25. package/dist/agent/memory-recall.js.map +1 -1
  26. package/dist/agent/memory-usage.d.ts +20 -0
  27. package/dist/agent/memory-usage.js +16 -1
  28. package/dist/agent/memory-usage.js.map +1 -1
  29. package/dist/agent/memory.d.ts +3 -0
  30. package/dist/agent/memory.js +24 -4
  31. package/dist/agent/memory.js.map +1 -1
  32. package/dist/agent/skills.d.ts +16 -3
  33. package/dist/agent/skills.js +36 -2
  34. package/dist/agent/skills.js.map +1 -1
  35. package/dist/agent/store.d.ts +7 -0
  36. package/dist/agent/store.js +14 -1
  37. package/dist/agent/store.js.map +1 -1
  38. package/dist/agent/subagent.d.ts +15 -2
  39. package/dist/agent/subagent.js +70 -16
  40. package/dist/agent/subagent.js.map +1 -1
  41. package/dist/config-files.d.ts +6 -1
  42. package/dist/config-files.js +7 -2
  43. package/dist/config-files.js.map +1 -1
  44. package/dist/config.d.ts +9 -0
  45. package/dist/config.js +2 -0
  46. package/dist/config.js.map +1 -1
  47. package/dist/foreign.d.ts +81 -0
  48. package/dist/foreign.js +198 -0
  49. package/dist/foreign.js.map +1 -0
  50. package/dist/harness.d.ts +47 -0
  51. package/dist/harness.js +186 -13
  52. package/dist/harness.js.map +1 -1
  53. package/dist/mcp/registry.d.ts +2 -0
  54. package/dist/mcp/registry.js +1 -0
  55. package/dist/mcp/registry.js.map +1 -1
  56. package/dist/mcp/registry.json +394 -1
  57. package/dist/paths.d.ts +19 -0
  58. package/dist/paths.js +38 -0
  59. package/dist/paths.js.map +1 -1
  60. package/dist/plugin-market.d.ts +57 -0
  61. package/dist/plugin-market.js +229 -0
  62. package/dist/plugin-market.js.map +1 -0
  63. package/dist/plugin-market.json +1246 -0
  64. package/dist/plugins.d.ts +49 -2
  65. package/dist/plugins.js +252 -10
  66. package/dist/plugins.js.map +1 -1
  67. package/dist/runtime-plugins/builtin/dreaming-coordinator.d.ts +244 -0
  68. package/dist/runtime-plugins/builtin/dreaming-coordinator.js +548 -0
  69. package/dist/runtime-plugins/builtin/dreaming-coordinator.js.map +1 -0
  70. package/dist/runtime-plugins/builtin/dreaming.d.ts +30 -0
  71. package/dist/runtime-plugins/builtin/dreaming.js +231 -0
  72. package/dist/runtime-plugins/builtin/dreaming.js.map +1 -0
  73. package/dist/runtime-plugins/builtin/memory.d.ts +89 -2
  74. package/dist/runtime-plugins/builtin/memory.js +186 -5
  75. package/dist/runtime-plugins/builtin/memory.js.map +1 -1
  76. package/dist/runtime-plugins/host.js +2 -0
  77. package/dist/runtime-plugins/host.js.map +1 -1
  78. package/dist/util/toml.d.ts +22 -0
  79. package/dist/util/toml.js +305 -0
  80. package/dist/util/toml.js.map +1 -0
  81. 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, { showScope: !!user });
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
- policy: () => policy.autoExtract,
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 = {