@oharness/engine 0.5.0 → 0.6.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 +63 -0
- package/dist/agent/agent.js +257 -7
- package/dist/agent/agent.js.map +1 -1
- package/dist/agent/memory-backup.d.ts +38 -0
- package/dist/agent/memory-backup.js +126 -0
- package/dist/agent/memory-backup.js.map +1 -0
- package/dist/agent/memory-consolidate.d.ts +47 -0
- package/dist/agent/memory-consolidate.js +118 -0
- package/dist/agent/memory-consolidate.js.map +1 -0
- package/dist/agent/memory-errors.d.ts +73 -0
- package/dist/agent/memory-errors.js +74 -0
- package/dist/agent/memory-errors.js.map +1 -0
- package/dist/agent/memory-extract.d.ts +36 -0
- package/dist/agent/memory-extract.js +169 -0
- package/dist/agent/memory-extract.js.map +1 -0
- package/dist/agent/memory-limits.d.ts +115 -0
- package/dist/agent/memory-limits.js +109 -0
- package/dist/agent/memory-limits.js.map +1 -0
- package/dist/agent/memory-links.d.ts +21 -0
- package/dist/agent/memory-links.js +45 -0
- package/dist/agent/memory-links.js.map +1 -0
- package/dist/agent/memory-merge.d.ts +34 -0
- package/dist/agent/memory-merge.js +152 -0
- package/dist/agent/memory-merge.js.map +1 -0
- package/dist/agent/memory-provider.d.ts +73 -0
- package/dist/agent/memory-provider.js +2 -0
- package/dist/agent/memory-provider.js.map +1 -0
- package/dist/agent/memory-recall.d.ts +72 -0
- package/dist/agent/memory-recall.js +192 -0
- package/dist/agent/memory-recall.js.map +1 -0
- package/dist/agent/memory-redact.d.ts +15 -0
- package/dist/agent/memory-redact.js +48 -0
- package/dist/agent/memory-redact.js.map +1 -0
- package/dist/agent/memory-review.d.ts +47 -0
- package/dist/agent/memory-review.js +127 -0
- package/dist/agent/memory-review.js.map +1 -0
- package/dist/agent/memory-usage.d.ts +24 -0
- package/dist/agent/memory-usage.js +68 -0
- package/dist/agent/memory-usage.js.map +1 -0
- package/dist/agent/memory.d.ts +137 -14
- package/dist/agent/memory.js +431 -130
- package/dist/agent/memory.js.map +1 -1
- package/dist/agent/permissions.d.ts +10 -2
- package/dist/agent/permissions.js +22 -3
- package/dist/agent/permissions.js.map +1 -1
- package/dist/agent/session.d.ts +8 -1
- package/dist/agent/session.js +9 -0
- package/dist/agent/session.js.map +1 -1
- package/dist/agent/store.d.ts +3 -0
- package/dist/agent/store.js.map +1 -1
- package/dist/agent/titles.js +2 -2
- package/dist/config.d.ts +7 -1
- package/dist/config.js +27 -0
- package/dist/config.js.map +1 -1
- package/dist/core/audit.d.ts +19 -0
- package/dist/core/types.d.ts +6 -0
- package/dist/core/types.js +12 -0
- package/dist/core/types.js.map +1 -1
- package/dist/harness.d.ts +7 -1
- package/dist/harness.js +33 -2
- package/dist/harness.js.map +1 -1
- package/dist/providers/builtin.d.ts +20 -0
- package/dist/providers/builtin.js +82 -0
- package/dist/providers/builtin.js.map +1 -1
- package/dist/providers/catalog.js +30 -3
- package/dist/providers/catalog.js.map +1 -1
- package/dist/providers/index.d.ts +2 -2
- package/dist/providers/index.js +1 -1
- package/dist/providers/index.js.map +1 -1
- package/dist/providers/piai.js +17 -8
- package/dist/providers/piai.js.map +1 -1
- package/dist/providers/registry.d.ts +5 -0
- package/dist/providers/registry.js +10 -2
- package/dist/providers/registry.js.map +1 -1
- package/dist/providers/types.d.ts +31 -0
- package/dist/providers/types.js +36 -0
- package/dist/providers/types.js.map +1 -1
- package/dist/runtime-plugins/builtin/memory-coordinator.d.ts +79 -0
- package/dist/runtime-plugins/builtin/memory-coordinator.js +169 -0
- package/dist/runtime-plugins/builtin/memory-coordinator.js.map +1 -0
- package/dist/runtime-plugins/builtin/memory-migrate.d.ts +35 -0
- package/dist/runtime-plugins/builtin/memory-migrate.js +141 -0
- package/dist/runtime-plugins/builtin/memory-migrate.js.map +1 -0
- package/dist/runtime-plugins/builtin/memory.d.ts +144 -5
- package/dist/runtime-plugins/builtin/memory.js +1016 -112
- package/dist/runtime-plugins/builtin/memory.js.map +1 -1
- package/dist/runtime-plugins/builtin/studio.js +19 -4
- package/dist/runtime-plugins/builtin/studio.js.map +1 -1
- package/dist/runtime-plugins/cloud-connector.js +4 -1
- package/dist/runtime-plugins/cloud-connector.js.map +1 -1
- package/dist/runtime-plugins/extensions.d.ts +9 -0
- package/dist/runtime-plugins/extensions.js +15 -0
- package/dist/runtime-plugins/extensions.js.map +1 -1
- package/dist/runtime-plugins/host.js +24 -2
- package/dist/runtime-plugins/host.js.map +1 -1
- package/dist/runtime-plugins/types.d.ts +6 -0
- package/dist/tools/builtin/ask.d.ts +9 -0
- package/dist/tools/builtin/ask.js +71 -0
- package/dist/tools/builtin/ask.js.map +1 -0
- package/dist/tools/builtin/index.d.ts +1 -0
- package/dist/tools/builtin/index.js +3 -0
- package/dist/tools/builtin/index.js.map +1 -1
- package/dist/tools/builtin/memory.d.ts +5 -5
- package/dist/tools/builtin/memory.js +151 -40
- package/dist/tools/builtin/memory.js.map +1 -1
- package/dist/tools/index.d.ts +2 -0
- package/dist/tools/index.js +1 -0
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/questions.d.ts +89 -0
- package/dist/tools/questions.js +97 -0
- package/dist/tools/questions.js.map +1 -0
- package/dist/tools/types.d.ts +7 -0
- package/dist/tools/types.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { constants } from "node:fs";
|
|
2
|
+
import { copyFile, lstat, mkdir, readdir, stat, writeFile } from "node:fs/promises";
|
|
3
|
+
import { createHash } from "node:crypto";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { readPluginFile, readUserFile, updateRuntimePluginConfig, writeUserFile } from "../../config-files.js";
|
|
6
|
+
import { userPath } from "../../paths.js";
|
|
7
|
+
import { projectId } from "../../storage/project.js";
|
|
8
|
+
/**
|
|
9
|
+
* Where memory lives, and moving it there from where it used to.
|
|
10
|
+
*
|
|
11
|
+
* Notes used to sit in `<project>/.oharness/memory`, inside the repository
|
|
12
|
+
* tree: one copy per clone and per worktree, one `git add .` away from being
|
|
13
|
+
* committed for everyone, and gone with the folder. They now sit under the
|
|
14
|
+
* user directory, keyed by the same project id that sessions and cloud sync
|
|
15
|
+
* already use, so every clone of a repository shares one set.
|
|
16
|
+
*/
|
|
17
|
+
/** Written into a project's notes directory once its old notes have been considered. */
|
|
18
|
+
export const LEGACY_MARKER = ".imported-legacy.json";
|
|
19
|
+
export function legacyMemoryDirectory(cwd) {
|
|
20
|
+
return path.join(cwd, ".oharness", "memory");
|
|
21
|
+
}
|
|
22
|
+
export async function resolveMemoryDirectories(options) {
|
|
23
|
+
const legacy = legacyMemoryDirectory(options.cwd);
|
|
24
|
+
let user = userPath("memory", "user");
|
|
25
|
+
try {
|
|
26
|
+
await mkdir(user, { recursive: true });
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
options.logger?.warn("User-level memory unavailable: the user directory is not writable", { error: String(error) });
|
|
30
|
+
user = undefined;
|
|
31
|
+
}
|
|
32
|
+
if (options.configured) {
|
|
33
|
+
return { project: path.resolve(options.cwd, options.configured), ...(user ? { user } : {}), migrated: 0 };
|
|
34
|
+
}
|
|
35
|
+
const target = userPath("memory", "projects", await projectId(options.cwd));
|
|
36
|
+
try {
|
|
37
|
+
await mkdir(target, { recursive: true });
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
// A container whose home is a read-only layer still has a project volume.
|
|
41
|
+
options.logger?.warn("Memory stays in the project folder: the user directory is not writable", { error: String(error) });
|
|
42
|
+
return { project: legacy, ...(user ? { user } : {}), migrated: 0 };
|
|
43
|
+
}
|
|
44
|
+
let migrated = 0;
|
|
45
|
+
try {
|
|
46
|
+
migrated = await importLegacyMemory(legacy, target);
|
|
47
|
+
if (migrated)
|
|
48
|
+
options.logger?.info("Moved memory notes into the user directory", { count: migrated, from: legacy, to: target });
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
options.logger?.warn("Could not import memory notes from the project folder", { error: String(error) });
|
|
52
|
+
}
|
|
53
|
+
return { project: target, ...(user ? { user } : {}), migrated };
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Copy notes from the old directory into the new one, once.
|
|
57
|
+
*
|
|
58
|
+
* Only into an empty directory: merging two sets of notes is sync's job, with
|
|
59
|
+
* its conflict handling, not a startup step's. The marker makes it once — a
|
|
60
|
+
* note someone later deletes in the new place must not return from the old
|
|
61
|
+
* one on the next launch. The old directory is left untouched.
|
|
62
|
+
*/
|
|
63
|
+
export async function importLegacyMemory(legacy, target) {
|
|
64
|
+
const marker = path.join(target, LEGACY_MARKER);
|
|
65
|
+
if (await exists(marker))
|
|
66
|
+
return 0;
|
|
67
|
+
let names;
|
|
68
|
+
try {
|
|
69
|
+
if ((await lstat(legacy)).isSymbolicLink())
|
|
70
|
+
return 0;
|
|
71
|
+
names = (await readdir(legacy)).filter((name) => name.endsWith(".md"));
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
return 0;
|
|
75
|
+
}
|
|
76
|
+
if (!names.length)
|
|
77
|
+
return 0;
|
|
78
|
+
const existing = (await readdir(target)).filter((name) => name.endsWith(".md"));
|
|
79
|
+
if (existing.length) {
|
|
80
|
+
await writeFile(marker, JSON.stringify({ from: legacy, skipped: "target already had notes", at: Date.now() }) + "\n");
|
|
81
|
+
return 0;
|
|
82
|
+
}
|
|
83
|
+
let copied = 0;
|
|
84
|
+
for (const name of names) {
|
|
85
|
+
const from = path.join(legacy, name);
|
|
86
|
+
const info = await lstat(from);
|
|
87
|
+
if (info.isSymbolicLink() || !info.isFile())
|
|
88
|
+
continue;
|
|
89
|
+
try {
|
|
90
|
+
await copyFile(from, path.join(target, name), constants.COPYFILE_EXCL);
|
|
91
|
+
copied++;
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
if (error.code !== "EEXIST")
|
|
95
|
+
throw error;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
await carrySyncSettings(legacy, target);
|
|
99
|
+
await writeFile(marker, JSON.stringify({ from: legacy, copied, at: Date.now() }) + "\n");
|
|
100
|
+
return copied;
|
|
101
|
+
}
|
|
102
|
+
const digest = (value) => createHash("sha256").update(value).digest("hex");
|
|
103
|
+
const object = (x) => !!x && typeof x === "object" && !Array.isArray(x);
|
|
104
|
+
/**
|
|
105
|
+
* Sync consent is keyed by directory. Without carrying it, a person who had
|
|
106
|
+
* switched cloud sync off for the old directory would find it silently on for
|
|
107
|
+
* the new one — the one outcome of a migration that cannot be undone, since
|
|
108
|
+
* an upload cannot be recalled. The baseline goes with it, so the first sync
|
|
109
|
+
* afterwards reconciles rather than treating every note as new.
|
|
110
|
+
*/
|
|
111
|
+
async function carrySyncSettings(legacy, target) {
|
|
112
|
+
const from = digest(path.resolve(legacy));
|
|
113
|
+
const to = digest(path.resolve(target));
|
|
114
|
+
const config = readPluginFile()?.runtimePlugins?.find((plugin) => plugin.id === "memory")?.config;
|
|
115
|
+
const consent = object(config) && object(config.syncTargets) ? config.syncTargets[from] : undefined;
|
|
116
|
+
if (!object(consent))
|
|
117
|
+
return;
|
|
118
|
+
await updateRuntimePluginConfig("memory", (current) => {
|
|
119
|
+
const targets = object(current.syncTargets) ? current.syncTargets : {};
|
|
120
|
+
if (targets[to])
|
|
121
|
+
return current;
|
|
122
|
+
return { ...current, syncTargets: { ...targets, [to]: consent } };
|
|
123
|
+
});
|
|
124
|
+
if (typeof consent.userId === "string" && consent.userId && typeof consent.origin === "string") {
|
|
125
|
+
const oldState = `memory-sync/${digest(`${consent.origin}:${consent.userId}:${from}`)}.json`;
|
|
126
|
+
const newState = `memory-sync/${digest(`${consent.origin}:${consent.userId}:${to}`)}.json`;
|
|
127
|
+
const state = readUserFile(oldState);
|
|
128
|
+
if (state && !readUserFile(newState))
|
|
129
|
+
await writeUserFile(newState, state);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
async function exists(file) {
|
|
133
|
+
try {
|
|
134
|
+
await stat(file);
|
|
135
|
+
return true;
|
|
136
|
+
}
|
|
137
|
+
catch {
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
//# sourceMappingURL=memory-migrate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory-migrate.js","sourceRoot":"","sources":["../../../src/runtime-plugins/builtin/memory-migrate.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,19 +1,64 @@
|
|
|
1
1
|
import { MemoryStore } from "../../agent/memory.ts";
|
|
2
|
-
import type { MemoryNote } from "../../agent/memory.ts";
|
|
2
|
+
import type { MemoryNote, MemoryStores } from "../../agent/memory.ts";
|
|
3
|
+
import type { MemoryPolicy, MemoryScope } from "../../agent/memory-limits.ts";
|
|
4
|
+
import type { BackupReason } from "../../agent/memory-backup.ts";
|
|
5
|
+
import type { MemoryFailureInfo } from "../../agent/memory-errors.ts";
|
|
6
|
+
import type { MergeField } from "../../agent/memory-merge.ts";
|
|
7
|
+
import type { ExtractedNote, ExtractionResult } from "../../agent/memory-provider.ts";
|
|
3
8
|
import type { RuntimePlugin } from "../types.ts";
|
|
9
|
+
import { MemorySyncCoordinator } from "./memory-coordinator.ts";
|
|
4
10
|
type Identity = {
|
|
5
11
|
userId: string;
|
|
6
12
|
origin: string;
|
|
7
13
|
};
|
|
14
|
+
/** What the sync server says it will accept, for this account. Announced by the server; never assumed here. */
|
|
15
|
+
export interface SyncQuota {
|
|
16
|
+
maxNotes?: number;
|
|
17
|
+
maxChanges?: number;
|
|
18
|
+
maxNoteBytes?: number;
|
|
19
|
+
tombstoneDays?: number;
|
|
20
|
+
tier?: "free" | "paid";
|
|
21
|
+
}
|
|
22
|
+
type NoteRef = {
|
|
23
|
+
id: string;
|
|
24
|
+
summary: string;
|
|
25
|
+
};
|
|
26
|
+
/** What the last pass that brought anything down brought down, and which notes it merged on its own. */
|
|
27
|
+
export interface PullSummary {
|
|
28
|
+
at: number;
|
|
29
|
+
created: NoteRef[];
|
|
30
|
+
updated: NoteRef[];
|
|
31
|
+
deleted: NoteRef[];
|
|
32
|
+
merged?: NoteRef[];
|
|
33
|
+
}
|
|
8
34
|
interface Options {
|
|
9
35
|
cwd: string;
|
|
10
36
|
directory: string;
|
|
37
|
+
/** Which notes this service holds. User-level notes belong to no project. */
|
|
38
|
+
scope?: MemoryScope;
|
|
39
|
+
policy?: MemoryPolicy;
|
|
40
|
+
onWarning?(message: string): void;
|
|
11
41
|
identity(): Promise<Identity | undefined>;
|
|
12
42
|
token(): Promise<string | undefined>;
|
|
13
43
|
/** Same destination as session sync. The Cloud identity origin remains the consent pin. */
|
|
14
44
|
syncUrl?(): string;
|
|
15
45
|
fetch?: typeof fetch;
|
|
46
|
+
/** Background check interval while the directory is busy. Defaults to a minute. */
|
|
16
47
|
pollMs?: number;
|
|
48
|
+
/** Longest a quiet directory waits between checks. Defaults to five times `pollMs`. */
|
|
49
|
+
maxPollMs?: number;
|
|
50
|
+
/** Delay between a local write and the pass that uploads it. */
|
|
51
|
+
debounceMs?: number;
|
|
52
|
+
/** A project directory unused for this long is not checked in the background. */
|
|
53
|
+
activeWindowMs?: number;
|
|
54
|
+
/** Defaults to the process's shared coordinator. */
|
|
55
|
+
coordinator?: MemorySyncCoordinator;
|
|
56
|
+
}
|
|
57
|
+
export interface MemoryPluginOptions extends Omit<Options, "scope"> {
|
|
58
|
+
/** User-level notes directory. Omitted when it could not be created. */
|
|
59
|
+
userDirectory?: string;
|
|
60
|
+
/** One reply from the small model, for merge proposals. Without it merges are drafted by hand. */
|
|
61
|
+
generate?(system: string, prompt: string, signal?: AbortSignal): Promise<string>;
|
|
17
62
|
}
|
|
18
63
|
type Conflict = {
|
|
19
64
|
id: string;
|
|
@@ -21,50 +66,144 @@ type Conflict = {
|
|
|
21
66
|
cloud: MemoryNote | null;
|
|
22
67
|
localHash: string | null;
|
|
23
68
|
cloudHash: string | null;
|
|
69
|
+
/** The version both sides started from, when this machine kept a copy of it. */
|
|
70
|
+
base: MemoryNote | null;
|
|
71
|
+
/** What both sides changed in ways that could not be combined. */
|
|
72
|
+
fields: MergeField[];
|
|
24
73
|
};
|
|
25
|
-
/** One
|
|
74
|
+
/** One scope's local notes and explicit per-device/account sync consent. */
|
|
26
75
|
export declare class MemoryService {
|
|
27
76
|
readonly store: MemoryStore;
|
|
77
|
+
readonly scope: MemoryScope;
|
|
28
78
|
private readonly options;
|
|
29
79
|
private readonly target;
|
|
80
|
+
private readonly coordinator;
|
|
81
|
+
private readonly syncTarget;
|
|
82
|
+
private detach?;
|
|
30
83
|
private controller?;
|
|
31
|
-
private timer?;
|
|
32
84
|
private running?;
|
|
33
85
|
private closed;
|
|
34
86
|
private disabling;
|
|
35
87
|
private conflicts;
|
|
36
88
|
private error?;
|
|
37
89
|
private lastSync?;
|
|
90
|
+
private lastPull?;
|
|
91
|
+
private lastChanged;
|
|
38
92
|
private binding?;
|
|
39
93
|
private settingsRevision;
|
|
40
94
|
private retryAt;
|
|
41
95
|
private snapshotCache?;
|
|
96
|
+
private quota?;
|
|
97
|
+
/** The server predates `have`, so every snapshot carries every note. */
|
|
98
|
+
private legacyServer;
|
|
42
99
|
constructor(options: Options);
|
|
100
|
+
get isClosed(): boolean;
|
|
43
101
|
private consent;
|
|
44
102
|
private stateFile;
|
|
45
103
|
private allowed;
|
|
104
|
+
private eligible;
|
|
105
|
+
/**
|
|
106
|
+
* Hand this directory to the coordinator. It is checked straight away, then
|
|
107
|
+
* as the coordinator decides; a local write brings the next check forward.
|
|
108
|
+
* Calling it again does nothing.
|
|
109
|
+
*/
|
|
46
110
|
start(): void;
|
|
111
|
+
/** These notes are in use: check the cloud soon, if it has been a while. */
|
|
112
|
+
activity(): void;
|
|
47
113
|
status(): Promise<{
|
|
114
|
+
lastPull?: PullSummary | undefined;
|
|
115
|
+
quota?: SyncQuota | undefined;
|
|
116
|
+
scope: "user" | "project";
|
|
48
117
|
signedIn: boolean;
|
|
49
118
|
enabled: boolean;
|
|
50
119
|
ready: boolean;
|
|
51
120
|
syncing: boolean;
|
|
52
121
|
lastSync: number | undefined;
|
|
53
122
|
error: string | undefined;
|
|
123
|
+
failure: MemoryFailureInfo | undefined;
|
|
54
124
|
accountChanged: boolean;
|
|
55
125
|
conflicts: Conflict[];
|
|
56
126
|
location: string;
|
|
57
127
|
entries: (MemoryNote & {
|
|
58
128
|
hash: string | null;
|
|
59
129
|
})[];
|
|
130
|
+
omitted: number;
|
|
60
131
|
}>;
|
|
61
132
|
enable(enabled: boolean): Promise<void>;
|
|
62
133
|
sync(): Promise<void>;
|
|
134
|
+
/**
|
|
135
|
+
* One reconciliation. Resolves with whether anything went up or came down.
|
|
136
|
+
*
|
|
137
|
+
* A note both sides changed since the last sync is merged field by field
|
|
138
|
+
* when the changes do not touch (memory-merge.ts), and held back as a
|
|
139
|
+
* conflict only when they do. A held note stops nothing else: every other
|
|
140
|
+
* note still goes up and comes down, and the held one keeps its baseline
|
|
141
|
+
* until someone chooses.
|
|
142
|
+
*/
|
|
63
143
|
private perform;
|
|
64
|
-
|
|
144
|
+
/** Where copies of notes at their baseline are kept, beside the baseline file. */
|
|
145
|
+
private baseDir;
|
|
146
|
+
/** The last synced version of a note, only when it is exactly the one the baseline records. */
|
|
147
|
+
private readBase;
|
|
148
|
+
private writeBaseNote;
|
|
149
|
+
/** Keep the baseline copies in step with the baseline hashes, writing only what moved. */
|
|
150
|
+
private writeBases;
|
|
151
|
+
/**
|
|
152
|
+
* Write a batch cloud first, for changes made without anyone reviewing each
|
|
153
|
+
* note (consolidation).
|
|
154
|
+
*
|
|
155
|
+
* Every note in the batch must be fully in sync, the same here, in the cloud
|
|
156
|
+
* and in the baseline, or the batch is refused before anything is written.
|
|
157
|
+
* The commit carries the manifest version those checks read, so a change
|
|
158
|
+
* from another device in between is refused by the server rather than
|
|
159
|
+
* becoming a conflict. Only once the cloud has accepted the batch is it
|
|
160
|
+
* written here. It needs the network; there is no offline fallback.
|
|
161
|
+
*/
|
|
162
|
+
commitFirst(edits: ReadonlyArray<{
|
|
163
|
+
id: string;
|
|
164
|
+
note?: MemoryNote;
|
|
165
|
+
expectedHash: string | null;
|
|
166
|
+
}>, reason: BackupReason): Promise<void>;
|
|
167
|
+
/**
|
|
168
|
+
* Whether changes here should go through the cloud first: sync is on and
|
|
169
|
+
* bound to the account signed in now. Otherwise they are ordinary local
|
|
170
|
+
* edits that a later sync uploads.
|
|
171
|
+
*/
|
|
172
|
+
syncReady(): Promise<boolean>;
|
|
173
|
+
/** The plan the sync service last announced for this account, if any. */
|
|
174
|
+
get tier(): SyncQuota["tier"];
|
|
175
|
+
/** Largest note body this machine and the sync service will both accept. */
|
|
176
|
+
noteBytesLimit(): number;
|
|
177
|
+
/** Changes grouped to fit the server's announced change quota and a request size its body limit accepts. */
|
|
178
|
+
private batches;
|
|
179
|
+
private snapshot;
|
|
180
|
+
private commit;
|
|
181
|
+
/** A request carrying `have`, retried without it once against a server that predates the field. */
|
|
182
|
+
private call;
|
|
183
|
+
/**
|
|
184
|
+
* Settle a conflict. Whatever is set aside is backed up first, whichever side
|
|
185
|
+
* it came from, and the baseline moves to the cloud's version so the next
|
|
186
|
+
* sync uploads the outcome.
|
|
187
|
+
*
|
|
188
|
+
* `local` keeps this machine's version. `cloud` takes the cloud's. `both`
|
|
189
|
+
* keeps this machine's version under the note's id and the cloud's as a new
|
|
190
|
+
* note beside it. `manual` saves `merged`, written by a person from all three.
|
|
191
|
+
*/
|
|
192
|
+
resolve(id: string, choice: string, merged?: Record<string, unknown>): Promise<void>;
|
|
193
|
+
private readQuota;
|
|
65
194
|
private validateSnapshot;
|
|
66
195
|
private request;
|
|
67
196
|
close(): Promise<void>;
|
|
68
197
|
}
|
|
69
|
-
export declare function
|
|
198
|
+
export declare function acquireMemoryService(options: Options): {
|
|
199
|
+
service: MemoryService;
|
|
200
|
+
release(): Promise<void>;
|
|
201
|
+
};
|
|
202
|
+
/**
|
|
203
|
+
* Write what an extraction pass proposed. New notes first, so every
|
|
204
|
+
* `[[new:N]]` placeholder has an id to become; then the notes that used a
|
|
205
|
+
* placeholder are rewritten with it.
|
|
206
|
+
*/
|
|
207
|
+
export declare function commitExtracted(stores: MemoryStores, notes: readonly ExtractedNote[]): Promise<ExtractionResult>;
|
|
208
|
+
export declare function createMemoryPlugin(options: MemoryPluginOptions, onReady: (service: MemoryService, user?: MemoryService) => void): RuntimePlugin;
|
|
70
209
|
export {};
|