@glassly/engine 0.3.5-dev.772 → 0.3.5-dev.773
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/build/engine.d.ts +3 -0
- package/build/engine.d.ts.map +1 -1
- package/build/facades/memory.d.ts +10 -2
- package/build/facades/memory.d.ts.map +1 -1
- package/build/facades/memory.js +10 -0
- package/build/facades/memory.js.map +1 -1
- package/build/generated/releaseMetadata.js +3 -3
- package/build/generated/releaseMetadata.js.map +1 -1
- package/build/services/LocalMiniappRuntime.d.ts.map +1 -1
- package/build/services/LocalMiniappRuntime.js +19 -0
- package/build/services/LocalMiniappRuntime.js.map +1 -1
- package/build/services/MemoryStore.d.ts +13 -1
- package/build/services/MemoryStore.d.ts.map +1 -1
- package/build/services/MemoryStore.js +98 -1
- package/build/services/MemoryStore.js.map +1 -1
- package/build/services/memoryBackupFile.d.ts +9 -0
- package/build/services/memoryBackupFile.d.ts.map +1 -0
- package/build/services/memoryBackupFile.js +47 -0
- package/build/services/memoryBackupFile.js.map +1 -0
- package/package.json +7 -7
- package/src/facades/memory.ts +11 -2
- package/src/generated/releaseMetadata.ts +3 -3
- package/src/services/LocalMiniappRuntime.ts +19 -0
- package/src/services/MemoryStore.ts +111 -2
- package/src/services/memoryBackupFile.ts +50 -0
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* load it migrates Assist's per-app storage namespace into the shared
|
|
17
17
|
* user-scoped keys so existing history survives the move.
|
|
18
18
|
*/
|
|
19
|
-
import type { MemoryEntry, MemoryNoteSource, MemorySession, MemorySummary } from "@glassly/miniapp";
|
|
19
|
+
import type { MemoryBackup, MemoryEntry, MemoryImportResult, MemoryNoteSource, MemorySession, MemorySummary } from "@glassly/miniapp";
|
|
20
20
|
/**
|
|
21
21
|
* Physical bounds. There is no age cap: the timeline stays until the user
|
|
22
22
|
* deletes it (clear()/removeScope). The entry cap bounds the write cost —
|
|
@@ -51,6 +51,10 @@ export interface MemoryStats {
|
|
|
51
51
|
/** Epoch ms of the oldest entry, or null when empty. */
|
|
52
52
|
oldestAt: number | null;
|
|
53
53
|
}
|
|
54
|
+
/** Thrown by importBackup for anything that isn't a backup this build can read. */
|
|
55
|
+
export declare class MemoryBackupError extends Error {
|
|
56
|
+
constructor(message: string);
|
|
57
|
+
}
|
|
54
58
|
export declare function hourStartOf(ms: number): number;
|
|
55
59
|
export declare function dayStartOf(ms: number): number;
|
|
56
60
|
export declare class MemoryStore {
|
|
@@ -132,6 +136,14 @@ export declare class MemoryStore {
|
|
|
132
136
|
summaryList(): Promise<MemorySummary[]>;
|
|
133
137
|
/** Most recent COMPLETED hour with lines but no summary, or null. */
|
|
134
138
|
pendingSummaryHour(nowMs?: number): Promise<number | null>;
|
|
139
|
+
/** The whole timeline as a backup file body (Settings > Memory, session.memory.exportFile). */
|
|
140
|
+
exportBackup(): Promise<MemoryBackup>;
|
|
141
|
+
/**
|
|
142
|
+
* Merge a backup into the timeline. Skips entries whose id is stored, notes
|
|
143
|
+
* whose text is already in the same hour, and hours that already have a
|
|
144
|
+
* summary. Throws MemoryBackupError when `data` isn't a memory backup.
|
|
145
|
+
*/
|
|
146
|
+
importBackup(data: unknown): Promise<MemoryImportResult>;
|
|
135
147
|
/** Plain-text export of a scope, for the OS share sheet. */
|
|
136
148
|
exportText(scope: "session" | "day" | "all", at?: number): Promise<string>;
|
|
137
149
|
private ensureLoaded;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MemoryStore.d.ts","sourceRoot":"","sources":["../../src/services/MemoryStore.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"MemoryStore.d.ts","sourceRoot":"","sources":["../../src/services/MemoryStore.ts"],"names":[],"mappings":"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"}
|
|
@@ -56,6 +56,13 @@ const QUERY_LIMIT_DEFAULT = 60;
|
|
|
56
56
|
* window. Short enough that an unexpected kill loses only the last few lines.
|
|
57
57
|
*/
|
|
58
58
|
const DEFAULT_WRITE_DEBOUNCE_MS = 3000;
|
|
59
|
+
/** Thrown by importBackup for anything that isn't a backup this build can read. */
|
|
60
|
+
export class MemoryBackupError extends Error {
|
|
61
|
+
constructor(message) {
|
|
62
|
+
super(message);
|
|
63
|
+
this.name = "MemoryBackupError";
|
|
64
|
+
}
|
|
65
|
+
}
|
|
59
66
|
export function hourStartOf(ms) {
|
|
60
67
|
return Math.floor(ms / HOUR_MS) * HOUR_MS;
|
|
61
68
|
}
|
|
@@ -292,6 +299,62 @@ export class MemoryStore {
|
|
|
292
299
|
return null;
|
|
293
300
|
return Math.max(...hours);
|
|
294
301
|
}
|
|
302
|
+
/** The whole timeline as a backup file body (Settings > Memory, session.memory.exportFile). */
|
|
303
|
+
async exportBackup() {
|
|
304
|
+
await this.ensureLoaded();
|
|
305
|
+
return {
|
|
306
|
+
format: "glassly-memory",
|
|
307
|
+
version: 1,
|
|
308
|
+
exportedAt: Date.now(),
|
|
309
|
+
entries: [...this.entries],
|
|
310
|
+
summaries: [...this.summaries].sort((a, b) => a.hourStart - b.hourStart),
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Merge a backup into the timeline. Skips entries whose id is stored, notes
|
|
315
|
+
* whose text is already in the same hour, and hours that already have a
|
|
316
|
+
* summary. Throws MemoryBackupError when `data` isn't a memory backup.
|
|
317
|
+
*/
|
|
318
|
+
async importBackup(data) {
|
|
319
|
+
const backup = parseBackup(data);
|
|
320
|
+
await this.ensureLoaded();
|
|
321
|
+
const result = { entries: 0, summaries: 0, skipped: 0 };
|
|
322
|
+
const ids = new Set(this.entries.map((e) => e.id));
|
|
323
|
+
const noteKeys = new Set(this.entries.filter((e) => e.kind === "note").map(noteKey));
|
|
324
|
+
for (const entry of backup.entries) {
|
|
325
|
+
const key = entry.kind === "note" ? noteKey(entry) : null;
|
|
326
|
+
if (ids.has(entry.id) || (key != null && noteKeys.has(key))) {
|
|
327
|
+
result.skipped++;
|
|
328
|
+
continue;
|
|
329
|
+
}
|
|
330
|
+
ids.add(entry.id);
|
|
331
|
+
if (key != null)
|
|
332
|
+
noteKeys.add(key);
|
|
333
|
+
this.entries.push(entry);
|
|
334
|
+
result.entries++;
|
|
335
|
+
}
|
|
336
|
+
const byHour = new Map(this.summaries.map((s) => [s.hourStart, s]));
|
|
337
|
+
for (const summary of backup.summaries) {
|
|
338
|
+
if (byHour.get(summary.hourStart)?.text.trim()) {
|
|
339
|
+
result.skipped++;
|
|
340
|
+
continue;
|
|
341
|
+
}
|
|
342
|
+
byHour.set(summary.hourStart, summary);
|
|
343
|
+
result.summaries++;
|
|
344
|
+
}
|
|
345
|
+
if (result.entries > 0) {
|
|
346
|
+
this.entries.sort((a, b) => a.at - b.at);
|
|
347
|
+
this.prune();
|
|
348
|
+
await this.persistLog();
|
|
349
|
+
}
|
|
350
|
+
if (result.summaries > 0) {
|
|
351
|
+
this.summaries = [...byHour.values()].sort((a, b) => a.hourStart - b.hourStart).slice(-MEMORY_BOUNDS.maxSummaries);
|
|
352
|
+
await this.persistSummaries();
|
|
353
|
+
}
|
|
354
|
+
if (result.entries > 0 || result.summaries > 0)
|
|
355
|
+
this.emitChanged();
|
|
356
|
+
return result;
|
|
357
|
+
}
|
|
295
358
|
/** Plain-text export of a scope, for the OS share sheet. */
|
|
296
359
|
async exportText(scope, at) {
|
|
297
360
|
const sessions = (await this.sessionList()).filter((s) => {
|
|
@@ -494,9 +557,43 @@ function parseArray(raw) {
|
|
|
494
557
|
function normalize(text) {
|
|
495
558
|
return text
|
|
496
559
|
.toLowerCase()
|
|
497
|
-
.replace(/[
|
|
560
|
+
.replace(/[^\p{L}\p{N}]+/gu, " ")
|
|
498
561
|
.trim();
|
|
499
562
|
}
|
|
563
|
+
function noteKey(entry) {
|
|
564
|
+
return `${hourStartOf(entry.at)}:${normalize(entry.text)}`;
|
|
565
|
+
}
|
|
566
|
+
function parseBackup(data) {
|
|
567
|
+
const raw = data;
|
|
568
|
+
if (!raw || typeof raw !== "object" || raw.format !== "glassly-memory" || !Array.isArray(raw.entries)) {
|
|
569
|
+
throw new MemoryBackupError("Not a Glassly memory backup");
|
|
570
|
+
}
|
|
571
|
+
if (typeof raw.version !== "number" || raw.version > 1) {
|
|
572
|
+
throw new MemoryBackupError(`Unsupported memory backup version ${String(raw.version)}`);
|
|
573
|
+
}
|
|
574
|
+
const entries = [];
|
|
575
|
+
for (const v of raw.entries) {
|
|
576
|
+
if (!isEntry(v) || !v.id || !v.text.trim() || !Number.isFinite(v.at))
|
|
577
|
+
continue;
|
|
578
|
+
entries.push({
|
|
579
|
+
id: v.id,
|
|
580
|
+
kind: v.kind,
|
|
581
|
+
text: v.text.trim(),
|
|
582
|
+
at: v.at,
|
|
583
|
+
speakerId: typeof v.speakerId === "string" ? v.speakerId : null,
|
|
584
|
+
...(typeof v.resolvedSpeakerName === "string" && v.resolvedSpeakerName
|
|
585
|
+
? { resolvedSpeakerName: v.resolvedSpeakerName }
|
|
586
|
+
: {}),
|
|
587
|
+
language: typeof v.language === "string" ? v.language : null,
|
|
588
|
+
...(v.kind === "note" ? { source: v.source === "voice" || v.source === "auto" ? v.source : "ui" } : {}),
|
|
589
|
+
});
|
|
590
|
+
}
|
|
591
|
+
const summaries = (Array.isArray(raw.summaries) ? raw.summaries : [])
|
|
592
|
+
.filter(isSummary)
|
|
593
|
+
.filter((s) => s.text.trim() && Number.isFinite(s.hourStart))
|
|
594
|
+
.map((s) => ({ hourStart: s.hourStart, text: s.text, createdAt: s.createdAt }));
|
|
595
|
+
return { entries, summaries };
|
|
596
|
+
}
|
|
500
597
|
function isEntry(v) {
|
|
501
598
|
if (!v || typeof v !== "object")
|
|
502
599
|
return false;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MemoryStore.js","sourceRoot":"","sources":["../../src/services/MemoryStore.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * MemoryStore — the user's shared, phone-local memory timeline.\n *\n * One unified per-hour stream of what happened around the user — spoken\n * transcript lines AND notes (manual, voice, auto) in one timeline — plus the\n * hourly conversation summary that heads each session. Scoped to the Glassly\n * USER (not to a miniapp): every miniapp that declares the MEMORY manifest\n * permission reads and writes the same store through LocalMiniappRuntime's\n * MEMORY_* handlers, and Settings > Memory manages it via engine.memory.\n *\n * A \"session\" is one clock hour that has entries, derived from entry\n * timestamps at read time — nothing is stored per-session, so there is no\n * session id to keep in sync.\n *\n * The store began life inside the Assist miniapp (its SessionStore); on first\n * load it migrates Assist's per-app storage namespace into the shared\n * user-scoped keys so existing history survives the move.\n */\n\nimport type {MemoryEntry, MemoryNoteSource, MemorySession, MemorySummary} from \"@glassly/miniapp\"\n\nimport {searchMemoryEntries} from \"../utils/memory/search\"\n\nimport {BgTimer} from \"../utils/timers\"\n\nconst KEY_ROOT = \"memory_\"\nconst LOG_SUFFIX = \"log\"\nconst SUMMARY_SUFFIX = \"summaries\"\n\n/**\n * The pre-memory-layer Assist miniapp whose storage namespace we migrate.\n * Assist now ships as com.glassly.assistant; the timeline it reads is\n * user-scoped (KEY_ROOT below), so the rename needs no second migration and\n * this constant stays pinned to the old namespace.\n */\nconst LEGACY_ASSIST_PACKAGE = \"com.tetramo.assistant\"\nconst LEGACY_STORAGE_ROOT = \"localstorage_\"\nconst LEGACY_LOG_KEY = \"assistant:session-log\"\nconst LEGACY_SUMMARY_KEY = \"assistant:hourly-summaries\"\n// Assist's own pre-unification keys, migrated for users who never ran the\n// unified-schema Assist build.\nconst LEGACY_TRANSCRIPT_KEY = \"assistant:transcript-log\"\nconst LEGACY_NOTES_KEY = \"assistant:notes\"\n\nconst HOUR_MS = 60 * 60 * 1000\n\n/**\n * Physical bounds. There is no age cap: the timeline stays until the user\n * deletes it (clear()/removeScope). The entry cap bounds the write cost —\n * every persist re-serializes the whole timeline, so it is a perf ceiling as\n * much as a product number — and the summary cap keeps one row per hour for\n * about half a year of round-the-clock use, far longer at a normal pace.\n */\nexport const MEMORY_BOUNDS = {\n maxEntries: 24_000,\n maxSummaries: 4320,\n}\nconst QUERY_LIMIT_CAP = 200\nconst QUERY_LIMIT_DEFAULT = 60\n/**\n * How long transcript appends coalesce before hitting disk. Each write\n * re-serializes the entire timeline, so at conversational pace (a final\n * utterance every few seconds) this turns O(n) work per line into O(n) per\n * window. Short enough that an unexpected kill loses only the last few lines.\n */\nconst DEFAULT_WRITE_DEBOUNCE_MS = 3000\n\nexport interface MemoryStoreBackend {\n get(key: string): unknown | null\n set(key: string, value: unknown): void\n remove(key: string): void\n}\n\nexport interface MemoryStoreHooks {\n getUserId: () => Promise<string>\n backend: MemoryStoreBackend\n /**\n * Debounce window for transcript-line writes, in ms. Appends coalesce into\n * one serialization instead of re-stringifying the whole timeline per\n * utterance. Set to 0 to persist synchronously (tests).\n */\n writeDebounceMs?: number\n}\n\nexport interface MemoryStats {\n entryCount: number\n lineCount: number\n noteCount: number\n summaryCount: number\n /** Epoch ms of the oldest entry, or null when empty. */\n oldestAt: number | null\n}\n\nexport function hourStartOf(ms: number): number {\n return Math.floor(ms / HOUR_MS) * HOUR_MS\n}\n\nexport function dayStartOf(ms: number): number {\n const d = new Date(ms)\n d.setHours(0, 0, 0, 0)\n return d.getTime()\n}\n\nfunction makeId(): string {\n return `e-${Date.now()}-${Math.random().toString(36).slice(2)}`\n}\n\nexport class MemoryStore {\n private entries: MemoryEntry[] = []\n private summaries: MemorySummary[] = []\n /** User the in-memory cache was loaded for; reload when identity changes. */\n private loadedUserId: string | null = null\n private loadPromise: Promise<void> | null = null\n private readonly changeListeners = new Set<() => void>()\n /** Pending debounced log write (transcript appends), if any. */\n private logFlushTimer: number | null = null\n private logDirty = false\n\n constructor(private readonly hooks: MemoryStoreHooks) {}\n\n /**\n * Write any debounced changes out now. Call before the app backgrounds or\n * terminates so a crash can't lose the coalesced tail of the timeline.\n */\n async flush(): Promise<void> {\n if (this.logFlushTimer) {\n BgTimer.clearTimeout(this.logFlushTimer)\n this.logFlushTimer = null\n }\n if (this.logDirty) await this.persistLog()\n }\n\n /** Subscribe to mutations (adds, edits, deletes, clears). */\n onChanged(listener: () => void): () => void {\n this.changeListeners.add(listener)\n return () => this.changeListeners.delete(listener)\n }\n\n // === Writes ===\n\n async appendLine(input: {\n text: string\n speakerId: string | null\n /** Resolved enrolled-speaker name (on-device speaker ID), when known. */\n resolvedSpeakerName?: string | null\n language: string | null\n at: number\n }): Promise<void> {\n if (!input.text.trim()) return\n await this.ensureLoaded()\n this.entries.push({\n id: makeId(),\n kind: \"line\",\n text: input.text.trim(),\n at: input.at,\n speakerId: input.speakerId,\n ...(input.resolvedSpeakerName ? {resolvedSpeakerName: input.resolvedSpeakerName} : {}),\n language: input.language,\n })\n this.prune()\n // Transcript lines are the hot path (one per final utterance) and each\n // persist re-serializes the whole timeline, so coalesce them. Everything\n // else — notes, edits, deletes, summaries — still writes through\n // immediately; those are user-visible and rare.\n this.schedulePersistLog()\n this.emitChanged()\n }\n\n /**\n * Add a note. `at` targets the hour it belongs to — pass a timestamp inside\n * a past hour to append to that session. Dedupes on normalized text within\n * the same hour (auto-notes re-extract); resolves null on duplicate/empty.\n */\n async addNote(text: string, source: MemoryNoteSource, at: number = Date.now()): Promise<MemoryEntry | null> {\n const body = text.trim()\n if (!body) return null\n await this.ensureLoaded()\n const hour = hourStartOf(at)\n const key = normalize(body)\n if (this.entries.some((e) => e.kind === \"note\" && hourStartOf(e.at) === hour && normalize(e.text) === key)) {\n return null\n }\n const entry: MemoryEntry = {id: makeId(), kind: \"note\", text: body, at, speakerId: null, language: null, source}\n this.entries.push(entry)\n this.entries.sort((a, b) => a.at - b.at)\n this.prune()\n await this.persistLog()\n this.emitChanged()\n return entry\n }\n\n /** Rewrite a note's text in place (Settings > Memory edit). */\n async updateNote(id: string, text: string): Promise<boolean> {\n const body = text.trim()\n if (!body) return false\n await this.ensureLoaded()\n const entry = this.entries.find((e) => e.id === id && e.kind === \"note\")\n if (!entry) return false\n entry.text = body\n await this.persistLog()\n this.emitChanged()\n return true\n }\n\n async removeEntry(id: string): Promise<boolean> {\n await this.ensureLoaded()\n const before = this.entries.length\n this.entries = this.entries.filter((e) => e.id !== id)\n if (this.entries.length === before) return false\n await this.persistLog()\n this.emitChanged()\n return true\n }\n\n /** Delete every entry (and summary) in an hour, or a whole day of hours. */\n async removeScope(scope: \"session\" | \"day\", at: number): Promise<void> {\n await this.ensureLoaded()\n const inScope =\n scope === \"day\"\n ? (ms: number) => dayStartOf(ms) === dayStartOf(at)\n : (ms: number) => hourStartOf(ms) === hourStartOf(at)\n this.entries = this.entries.filter((e) => !inScope(e.at))\n this.summaries = this.summaries.filter((s) => !inScope(s.hourStart))\n await this.persistLog()\n await this.persistSummaries()\n this.emitChanged()\n }\n\n /** Drop the entire timeline: every entry and every summary. */\n async clear(): Promise<void> {\n await this.ensureLoaded()\n this.entries = []\n this.summaries = []\n await this.persistLog()\n await this.persistSummaries()\n this.emitChanged()\n }\n\n async addSummary(summary: MemorySummary): Promise<void> {\n await this.ensureLoaded()\n const {maxSummaries} = MEMORY_BOUNDS\n this.summaries = this.summaries.filter((s) => s.hourStart !== summary.hourStart)\n this.summaries.push(summary)\n if (this.summaries.length > maxSummaries) {\n this.summaries = this.summaries.sort((a, b) => a.hourStart - b.hourStart).slice(-maxSummaries)\n }\n await this.persistSummaries()\n this.emitChanged()\n }\n\n // === Reads === (the whole stored timeline; nothing is hidden by age)\n\n /** Entries in [sinceMs, untilMs), newest last. */\n async window(sinceMs: number, untilMs: number = Date.now()): Promise<MemoryEntry[]> {\n await this.ensureLoaded()\n return this.entries.filter((e) => e.at >= sinceMs && e.at < untilMs)\n }\n\n /** All note entries, newest first (Settings > Memory list). */\n async notes(): Promise<MemoryEntry[]> {\n await this.ensureLoaded()\n return this.entries.filter((e) => e.kind === \"note\").sort((a, b) => b.at - a.at)\n }\n\n async stats(): Promise<MemoryStats> {\n await this.ensureLoaded()\n const visible = this.entries\n const noteCount = visible.filter((e) => e.kind === \"note\").length\n return {\n entryCount: visible.length,\n lineCount: visible.length - noteCount,\n noteCount,\n summaryCount: this.summaries.filter((s) => s.text.trim()).length,\n oldestAt: visible.length > 0 ? visible[0].at : null,\n }\n }\n\n /** All sessions (hours with entries), newest hour first. */\n async sessionList(): Promise<MemorySession[]> {\n await this.ensureLoaded()\n const byHour = new Map<number, MemoryEntry[]>()\n for (const e of this.entries) {\n const h = hourStartOf(e.at)\n const list = byHour.get(h) ?? []\n list.push(e)\n byHour.set(h, list)\n }\n const summaryByHour = new Map(this.summaries.map((s) => [s.hourStart, s.text]))\n return [...byHour.entries()]\n .map(([hourStart, entries]) => {\n entries.sort((a, b) => a.at - b.at)\n return {\n hourStart,\n entries,\n summary: (summaryByHour.get(hourStart) ?? \"\").trim() || null,\n lineCount: entries.filter((e) => e.kind === \"line\").length,\n noteCount: entries.filter((e) => e.kind === \"note\").length,\n }\n })\n .sort((a, b) => b.hourStart - a.hourStart)\n }\n\n /**\n * Query the FULL store by absolute-time window and keyword — the executor\n * behind LLM history-search tools. Newest last, so the limit keeps the most\n * recent matches. `contains` is an exact substring; `terms` is fuzzy any-of\n * matching (mistranscribed-name tolerant) — see utils/memory/search.ts.\n */\n async query(q: {\n sinceMs?: number\n untilMs?: number\n contains?: string\n terms?: string[]\n limit?: number\n }): Promise<MemoryEntry[]> {\n return (await this.search(q)).entries\n }\n\n /**\n * query() plus `windowCount` — how many entries the TIME window held before\n * keyword filtering, so an LLM caller can distinguish \"keyword matched\n * nothing\" from \"history doesn't reach that far back\" without a retry.\n */\n async search(q: {\n sinceMs?: number\n untilMs?: number\n contains?: string\n terms?: string[]\n limit?: number\n }): Promise<{entries: MemoryEntry[]; windowCount: number}> {\n await this.ensureLoaded()\n const limit = q.limit && q.limit > 0 ? Math.min(q.limit, QUERY_LIMIT_CAP) : QUERY_LIMIT_DEFAULT\n return searchMemoryEntries(this.entries, {\n sinceMs: q.sinceMs,\n untilMs: q.untilMs,\n contains: q.contains,\n terms: q.terms,\n limit,\n })\n }\n\n async summaryList(): Promise<MemorySummary[]> {\n await this.ensureLoaded()\n return [...this.summaries].sort((a, b) => b.hourStart - a.hourStart)\n }\n\n /** Most recent COMPLETED hour with lines but no summary, or null. */\n async pendingSummaryHour(nowMs: number = Date.now()): Promise<number | null> {\n await this.ensureLoaded()\n const currentHour = hourStartOf(nowMs)\n const summarized = new Set(this.summaries.map((s) => s.hourStart))\n const hours = new Set<number>()\n for (const e of this.entries) {\n if (e.kind !== \"line\") continue\n const h = hourStartOf(e.at)\n if (h < currentHour && !summarized.has(h)) hours.add(h)\n }\n if (hours.size === 0) return null\n return Math.max(...hours)\n }\n\n /** Plain-text export of a scope, for the OS share sheet. */\n async exportText(scope: \"session\" | \"day\" | \"all\", at?: number): Promise<string> {\n const sessions = (await this.sessionList()).filter((s) => {\n if (scope === \"all\") return true\n if (at == null) return false\n if (scope === \"day\") return dayStartOf(s.hourStart) === dayStartOf(at)\n return s.hourStart === hourStartOf(at)\n })\n if (sessions.length === 0) return \"No session history to export.\"\n\n const dayFmt = (ms: number) =>\n new Date(ms).toLocaleDateString(undefined, {weekday: \"long\", year: \"numeric\", month: \"long\", day: \"numeric\"})\n const hourFmt = (ms: number) => new Date(ms).toLocaleTimeString(undefined, {hour: \"numeric\"})\n const timeFmt = (ms: number) => new Date(ms).toLocaleTimeString(undefined, {hour: \"numeric\", minute: \"2-digit\"})\n\n const parts: string[] = []\n let lastDay: number | null = null\n for (const s of [...sessions].sort((a, b) => a.hourStart - b.hourStart)) {\n const day = dayStartOf(s.hourStart)\n if (day !== lastDay) {\n parts.push(`\\n=== ${dayFmt(day)} ===`)\n lastDay = day\n }\n parts.push(`\\n[${hourFmt(s.hourStart)}–${hourFmt(s.hourStart + HOUR_MS)}]`)\n if (s.summary) parts.push(`Summary: ${s.summary}`)\n for (const e of s.entries) {\n const who = e.kind === \"note\" ? \"NOTE\" : (e.speakerId ?? \"—\")\n parts.push(` ${timeFmt(e.at)} ${who}: ${e.text}`)\n }\n }\n return `Memory session history\\n${parts.join(\"\\n\").trim()}\\n`\n }\n\n // === Load / persist ===\n\n private async ensureLoaded(): Promise<void> {\n const userId = (await this.hooks.getUserId()).trim()\n if (!userId) throw new Error(\"Glassly user identity is unavailable\")\n if (this.loadedUserId === userId) return\n if (!this.loadPromise) {\n this.loadPromise = this.loadFor(userId).finally(() => {\n this.loadPromise = null\n })\n }\n await this.loadPromise\n }\n\n private async loadFor(userId: string): Promise<void> {\n // Don't let an identity switch discard lines still sitting in the debounce\n // window — they belong to the OUTGOING user, whose key is still loaded.\n await this.flush()\n this.entries = []\n this.summaries = []\n const rawLog = this.hooks.backend.get(this.key(userId, LOG_SUFFIX))\n if (rawLog != null) {\n this.entries = parseArray(rawLog).filter(isEntry)\n } else {\n this.migrateFromAssist(userId)\n }\n const rawSummaries = this.hooks.backend.get(this.key(userId, SUMMARY_SUFFIX))\n if (rawSummaries != null) {\n this.summaries = parseArray(rawSummaries).filter(isSummary)\n }\n this.loadedUserId = userId\n this.prune()\n }\n\n /**\n * One-time migration: fold the Assist miniapp's per-app store (the previous\n * home of this timeline) into the shared user-scoped keys. Assist's old keys\n * are left in place; its new build reads through the memory bridge instead.\n */\n private migrateFromAssist(userId: string): void {\n const legacyPrefix = `${LEGACY_STORAGE_ROOT}${userId}_${LEGACY_ASSIST_PACKAGE}_`\n try {\n const migrated: MemoryEntry[] = []\n const rawLog = this.hooks.backend.get(legacyPrefix + LEGACY_LOG_KEY)\n if (rawLog != null) {\n migrated.push(...parseArray(rawLog).filter(isEntry))\n } else {\n // Pre-unification Assist: separate transcript log + notes stores.\n for (const v of parseArray(this.hooks.backend.get(legacyPrefix + LEGACY_TRANSCRIPT_KEY))) {\n const e = v as Record<string, unknown>\n if (typeof e.text === \"string\" && typeof e.at === \"number\") {\n migrated.push({\n id: makeId(),\n kind: \"line\",\n text: e.text,\n at: e.at,\n speakerId: typeof e.speakerId === \"string\" ? e.speakerId : null,\n language: typeof e.language === \"string\" ? e.language : null,\n })\n }\n }\n for (const v of parseArray(this.hooks.backend.get(legacyPrefix + LEGACY_NOTES_KEY))) {\n const n = v as Record<string, unknown>\n if (typeof n.text === \"string\" && typeof n.createdAt === \"number\") {\n migrated.push({\n id: makeId(),\n kind: \"note\",\n text: n.text,\n at: n.createdAt,\n speakerId: null,\n language: null,\n source: n.source === \"voice\" || n.source === \"auto\" ? n.source : \"ui\",\n })\n }\n }\n }\n migrated.sort((a, b) => a.at - b.at)\n this.entries = migrated\n if (migrated.length > 0) this.hooks.backend.set(this.key(userId, LOG_SUFFIX), JSON.stringify(migrated))\n\n const rawSummaries = this.hooks.backend.get(legacyPrefix + LEGACY_SUMMARY_KEY)\n if (rawSummaries != null) {\n const summaries = parseArray(rawSummaries).filter(isSummary)\n this.summaries = summaries\n if (summaries.length > 0) this.hooks.backend.set(this.key(userId, SUMMARY_SUFFIX), JSON.stringify(summaries))\n }\n if (this.entries.length > 0 || this.summaries.length > 0) {\n console.log(\n `MemoryStore: migrated ${this.entries.length} entries + ${this.summaries.length} summaries from Assist`,\n )\n }\n } catch (err) {\n console.warn(\"MemoryStore: Assist migration failed:\", err instanceof Error ? err.message : err)\n }\n }\n\n /** Keep the newest MEMORY_BOUNDS.maxEntries; nothing goes for age — deleting is the user's call. */\n private prune(): void {\n const {maxEntries} = MEMORY_BOUNDS\n if (this.entries.length > maxEntries) this.entries = this.entries.slice(-maxEntries)\n }\n\n private key(userId: string, suffix: string): string {\n return `${KEY_ROOT}${userId}_${suffix}`\n }\n\n /**\n * Mark the log dirty and arm the debounce timer. Repeated appends inside the\n * window collapse into a single serialization; the first append after a\n * flush starts a fresh window (trailing-edge, so a steady stream of speech\n * still lands on disk every `writeDebounceMs`).\n */\n private schedulePersistLog(): void {\n const delay = this.hooks.writeDebounceMs ?? DEFAULT_WRITE_DEBOUNCE_MS\n if (delay <= 0) {\n void this.persistLog()\n return\n }\n this.logDirty = true\n if (this.logFlushTimer) return\n this.logFlushTimer = BgTimer.setTimeout(() => {\n this.logFlushTimer = null\n void this.persistLog().catch((err) =>\n console.warn(\"MemoryStore: debounced log write failed:\", err instanceof Error ? err.message : err),\n )\n }, delay)\n }\n\n private async persistLog(): Promise<void> {\n // Any write of the full array satisfies whatever the debounce was holding.\n this.logDirty = false\n if (this.logFlushTimer) {\n BgTimer.clearTimeout(this.logFlushTimer)\n this.logFlushTimer = null\n }\n if (this.loadedUserId == null) return\n this.hooks.backend.set(this.key(this.loadedUserId, LOG_SUFFIX), JSON.stringify(this.entries))\n }\n\n private async persistSummaries(): Promise<void> {\n if (this.loadedUserId == null) return\n this.hooks.backend.set(this.key(this.loadedUserId, SUMMARY_SUFFIX), JSON.stringify(this.summaries))\n }\n\n private emitChanged(): void {\n for (const listener of this.changeListeners) {\n try {\n listener()\n } catch (err) {\n console.warn(\"MemoryStore: onChanged listener threw:\", err instanceof Error ? err.message : err)\n }\n }\n }\n}\n\n/** Stored values may be a JSON string (SimpleStorage writes) or a raw array. */\nfunction parseArray(raw: unknown): unknown[] {\n try {\n if (typeof raw === \"string\") {\n const parsed = JSON.parse(raw) as unknown\n return Array.isArray(parsed) ? parsed : []\n }\n return Array.isArray(raw) ? raw : []\n } catch {\n return []\n }\n}\n\nfunction normalize(text: string): string {\n return text\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \" \")\n .trim()\n}\n\nfunction isEntry(v: unknown): v is MemoryEntry {\n if (!v || typeof v !== \"object\") return false\n const e = v as Record<string, unknown>\n return (\n typeof e.id === \"string\" &&\n (e.kind === \"line\" || e.kind === \"note\") &&\n typeof e.text === \"string\" &&\n typeof e.at === \"number\"\n )\n}\n\nfunction isSummary(v: unknown): v is MemorySummary {\n if (!v || typeof v !== \"object\") return false\n const s = v as Record<string, unknown>\n return typeof s.hourStart === \"number\" && typeof s.text === \"string\" && typeof s.createdAt === \"number\"\n}\n"]}
|
|
1
|
+
{"version":3,"file":"MemoryStore.js","sourceRoot":"","sources":["../../src/services/MemoryStore.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * MemoryStore — the user's shared, phone-local memory timeline.\n *\n * One unified per-hour stream of what happened around the user — spoken\n * transcript lines AND notes (manual, voice, auto) in one timeline — plus the\n * hourly conversation summary that heads each session. Scoped to the Glassly\n * USER (not to a miniapp): every miniapp that declares the MEMORY manifest\n * permission reads and writes the same store through LocalMiniappRuntime's\n * MEMORY_* handlers, and Settings > Memory manages it via engine.memory.\n *\n * A \"session\" is one clock hour that has entries, derived from entry\n * timestamps at read time — nothing is stored per-session, so there is no\n * session id to keep in sync.\n *\n * The store began life inside the Assist miniapp (its SessionStore); on first\n * load it migrates Assist's per-app storage namespace into the shared\n * user-scoped keys so existing history survives the move.\n */\n\nimport type {\n MemoryBackup,\n MemoryEntry,\n MemoryImportResult,\n MemoryNoteSource,\n MemorySession,\n MemorySummary,\n} from \"@glassly/miniapp\"\n\nimport {searchMemoryEntries} from \"../utils/memory/search\"\n\nimport {BgTimer} from \"../utils/timers\"\n\nconst KEY_ROOT = \"memory_\"\nconst LOG_SUFFIX = \"log\"\nconst SUMMARY_SUFFIX = \"summaries\"\n\n/**\n * The pre-memory-layer Assist miniapp whose storage namespace we migrate.\n * Assist now ships as com.glassly.assistant; the timeline it reads is\n * user-scoped (KEY_ROOT below), so the rename needs no second migration and\n * this constant stays pinned to the old namespace.\n */\nconst LEGACY_ASSIST_PACKAGE = \"com.tetramo.assistant\"\nconst LEGACY_STORAGE_ROOT = \"localstorage_\"\nconst LEGACY_LOG_KEY = \"assistant:session-log\"\nconst LEGACY_SUMMARY_KEY = \"assistant:hourly-summaries\"\n// Assist's own pre-unification keys, migrated for users who never ran the\n// unified-schema Assist build.\nconst LEGACY_TRANSCRIPT_KEY = \"assistant:transcript-log\"\nconst LEGACY_NOTES_KEY = \"assistant:notes\"\n\nconst HOUR_MS = 60 * 60 * 1000\n\n/**\n * Physical bounds. There is no age cap: the timeline stays until the user\n * deletes it (clear()/removeScope). The entry cap bounds the write cost —\n * every persist re-serializes the whole timeline, so it is a perf ceiling as\n * much as a product number — and the summary cap keeps one row per hour for\n * about half a year of round-the-clock use, far longer at a normal pace.\n */\nexport const MEMORY_BOUNDS = {\n maxEntries: 24_000,\n maxSummaries: 4320,\n}\nconst QUERY_LIMIT_CAP = 200\nconst QUERY_LIMIT_DEFAULT = 60\n/**\n * How long transcript appends coalesce before hitting disk. Each write\n * re-serializes the entire timeline, so at conversational pace (a final\n * utterance every few seconds) this turns O(n) work per line into O(n) per\n * window. Short enough that an unexpected kill loses only the last few lines.\n */\nconst DEFAULT_WRITE_DEBOUNCE_MS = 3000\n\nexport interface MemoryStoreBackend {\n get(key: string): unknown | null\n set(key: string, value: unknown): void\n remove(key: string): void\n}\n\nexport interface MemoryStoreHooks {\n getUserId: () => Promise<string>\n backend: MemoryStoreBackend\n /**\n * Debounce window for transcript-line writes, in ms. Appends coalesce into\n * one serialization instead of re-stringifying the whole timeline per\n * utterance. Set to 0 to persist synchronously (tests).\n */\n writeDebounceMs?: number\n}\n\nexport interface MemoryStats {\n entryCount: number\n lineCount: number\n noteCount: number\n summaryCount: number\n /** Epoch ms of the oldest entry, or null when empty. */\n oldestAt: number | null\n}\n\n/** Thrown by importBackup for anything that isn't a backup this build can read. */\nexport class MemoryBackupError extends Error {\n constructor(message: string) {\n super(message)\n this.name = \"MemoryBackupError\"\n }\n}\n\nexport function hourStartOf(ms: number): number {\n return Math.floor(ms / HOUR_MS) * HOUR_MS\n}\n\nexport function dayStartOf(ms: number): number {\n const d = new Date(ms)\n d.setHours(0, 0, 0, 0)\n return d.getTime()\n}\n\nfunction makeId(): string {\n return `e-${Date.now()}-${Math.random().toString(36).slice(2)}`\n}\n\nexport class MemoryStore {\n private entries: MemoryEntry[] = []\n private summaries: MemorySummary[] = []\n /** User the in-memory cache was loaded for; reload when identity changes. */\n private loadedUserId: string | null = null\n private loadPromise: Promise<void> | null = null\n private readonly changeListeners = new Set<() => void>()\n /** Pending debounced log write (transcript appends), if any. */\n private logFlushTimer: number | null = null\n private logDirty = false\n\n constructor(private readonly hooks: MemoryStoreHooks) {}\n\n /**\n * Write any debounced changes out now. Call before the app backgrounds or\n * terminates so a crash can't lose the coalesced tail of the timeline.\n */\n async flush(): Promise<void> {\n if (this.logFlushTimer) {\n BgTimer.clearTimeout(this.logFlushTimer)\n this.logFlushTimer = null\n }\n if (this.logDirty) await this.persistLog()\n }\n\n /** Subscribe to mutations (adds, edits, deletes, clears). */\n onChanged(listener: () => void): () => void {\n this.changeListeners.add(listener)\n return () => this.changeListeners.delete(listener)\n }\n\n // === Writes ===\n\n async appendLine(input: {\n text: string\n speakerId: string | null\n /** Resolved enrolled-speaker name (on-device speaker ID), when known. */\n resolvedSpeakerName?: string | null\n language: string | null\n at: number\n }): Promise<void> {\n if (!input.text.trim()) return\n await this.ensureLoaded()\n this.entries.push({\n id: makeId(),\n kind: \"line\",\n text: input.text.trim(),\n at: input.at,\n speakerId: input.speakerId,\n ...(input.resolvedSpeakerName ? {resolvedSpeakerName: input.resolvedSpeakerName} : {}),\n language: input.language,\n })\n this.prune()\n // Transcript lines are the hot path (one per final utterance) and each\n // persist re-serializes the whole timeline, so coalesce them. Everything\n // else — notes, edits, deletes, summaries — still writes through\n // immediately; those are user-visible and rare.\n this.schedulePersistLog()\n this.emitChanged()\n }\n\n /**\n * Add a note. `at` targets the hour it belongs to — pass a timestamp inside\n * a past hour to append to that session. Dedupes on normalized text within\n * the same hour (auto-notes re-extract); resolves null on duplicate/empty.\n */\n async addNote(text: string, source: MemoryNoteSource, at: number = Date.now()): Promise<MemoryEntry | null> {\n const body = text.trim()\n if (!body) return null\n await this.ensureLoaded()\n const hour = hourStartOf(at)\n const key = normalize(body)\n if (this.entries.some((e) => e.kind === \"note\" && hourStartOf(e.at) === hour && normalize(e.text) === key)) {\n return null\n }\n const entry: MemoryEntry = {id: makeId(), kind: \"note\", text: body, at, speakerId: null, language: null, source}\n this.entries.push(entry)\n this.entries.sort((a, b) => a.at - b.at)\n this.prune()\n await this.persistLog()\n this.emitChanged()\n return entry\n }\n\n /** Rewrite a note's text in place (Settings > Memory edit). */\n async updateNote(id: string, text: string): Promise<boolean> {\n const body = text.trim()\n if (!body) return false\n await this.ensureLoaded()\n const entry = this.entries.find((e) => e.id === id && e.kind === \"note\")\n if (!entry) return false\n entry.text = body\n await this.persistLog()\n this.emitChanged()\n return true\n }\n\n async removeEntry(id: string): Promise<boolean> {\n await this.ensureLoaded()\n const before = this.entries.length\n this.entries = this.entries.filter((e) => e.id !== id)\n if (this.entries.length === before) return false\n await this.persistLog()\n this.emitChanged()\n return true\n }\n\n /** Delete every entry (and summary) in an hour, or a whole day of hours. */\n async removeScope(scope: \"session\" | \"day\", at: number): Promise<void> {\n await this.ensureLoaded()\n const inScope =\n scope === \"day\"\n ? (ms: number) => dayStartOf(ms) === dayStartOf(at)\n : (ms: number) => hourStartOf(ms) === hourStartOf(at)\n this.entries = this.entries.filter((e) => !inScope(e.at))\n this.summaries = this.summaries.filter((s) => !inScope(s.hourStart))\n await this.persistLog()\n await this.persistSummaries()\n this.emitChanged()\n }\n\n /** Drop the entire timeline: every entry and every summary. */\n async clear(): Promise<void> {\n await this.ensureLoaded()\n this.entries = []\n this.summaries = []\n await this.persistLog()\n await this.persistSummaries()\n this.emitChanged()\n }\n\n async addSummary(summary: MemorySummary): Promise<void> {\n await this.ensureLoaded()\n const {maxSummaries} = MEMORY_BOUNDS\n this.summaries = this.summaries.filter((s) => s.hourStart !== summary.hourStart)\n this.summaries.push(summary)\n if (this.summaries.length > maxSummaries) {\n this.summaries = this.summaries.sort((a, b) => a.hourStart - b.hourStart).slice(-maxSummaries)\n }\n await this.persistSummaries()\n this.emitChanged()\n }\n\n // === Reads === (the whole stored timeline; nothing is hidden by age)\n\n /** Entries in [sinceMs, untilMs), newest last. */\n async window(sinceMs: number, untilMs: number = Date.now()): Promise<MemoryEntry[]> {\n await this.ensureLoaded()\n return this.entries.filter((e) => e.at >= sinceMs && e.at < untilMs)\n }\n\n /** All note entries, newest first (Settings > Memory list). */\n async notes(): Promise<MemoryEntry[]> {\n await this.ensureLoaded()\n return this.entries.filter((e) => e.kind === \"note\").sort((a, b) => b.at - a.at)\n }\n\n async stats(): Promise<MemoryStats> {\n await this.ensureLoaded()\n const visible = this.entries\n const noteCount = visible.filter((e) => e.kind === \"note\").length\n return {\n entryCount: visible.length,\n lineCount: visible.length - noteCount,\n noteCount,\n summaryCount: this.summaries.filter((s) => s.text.trim()).length,\n oldestAt: visible.length > 0 ? visible[0].at : null,\n }\n }\n\n /** All sessions (hours with entries), newest hour first. */\n async sessionList(): Promise<MemorySession[]> {\n await this.ensureLoaded()\n const byHour = new Map<number, MemoryEntry[]>()\n for (const e of this.entries) {\n const h = hourStartOf(e.at)\n const list = byHour.get(h) ?? []\n list.push(e)\n byHour.set(h, list)\n }\n const summaryByHour = new Map(this.summaries.map((s) => [s.hourStart, s.text]))\n return [...byHour.entries()]\n .map(([hourStart, entries]) => {\n entries.sort((a, b) => a.at - b.at)\n return {\n hourStart,\n entries,\n summary: (summaryByHour.get(hourStart) ?? \"\").trim() || null,\n lineCount: entries.filter((e) => e.kind === \"line\").length,\n noteCount: entries.filter((e) => e.kind === \"note\").length,\n }\n })\n .sort((a, b) => b.hourStart - a.hourStart)\n }\n\n /**\n * Query the FULL store by absolute-time window and keyword — the executor\n * behind LLM history-search tools. Newest last, so the limit keeps the most\n * recent matches. `contains` is an exact substring; `terms` is fuzzy any-of\n * matching (mistranscribed-name tolerant) — see utils/memory/search.ts.\n */\n async query(q: {\n sinceMs?: number\n untilMs?: number\n contains?: string\n terms?: string[]\n limit?: number\n }): Promise<MemoryEntry[]> {\n return (await this.search(q)).entries\n }\n\n /**\n * query() plus `windowCount` — how many entries the TIME window held before\n * keyword filtering, so an LLM caller can distinguish \"keyword matched\n * nothing\" from \"history doesn't reach that far back\" without a retry.\n */\n async search(q: {\n sinceMs?: number\n untilMs?: number\n contains?: string\n terms?: string[]\n limit?: number\n }): Promise<{entries: MemoryEntry[]; windowCount: number}> {\n await this.ensureLoaded()\n const limit = q.limit && q.limit > 0 ? Math.min(q.limit, QUERY_LIMIT_CAP) : QUERY_LIMIT_DEFAULT\n return searchMemoryEntries(this.entries, {\n sinceMs: q.sinceMs,\n untilMs: q.untilMs,\n contains: q.contains,\n terms: q.terms,\n limit,\n })\n }\n\n async summaryList(): Promise<MemorySummary[]> {\n await this.ensureLoaded()\n return [...this.summaries].sort((a, b) => b.hourStart - a.hourStart)\n }\n\n /** Most recent COMPLETED hour with lines but no summary, or null. */\n async pendingSummaryHour(nowMs: number = Date.now()): Promise<number | null> {\n await this.ensureLoaded()\n const currentHour = hourStartOf(nowMs)\n const summarized = new Set(this.summaries.map((s) => s.hourStart))\n const hours = new Set<number>()\n for (const e of this.entries) {\n if (e.kind !== \"line\") continue\n const h = hourStartOf(e.at)\n if (h < currentHour && !summarized.has(h)) hours.add(h)\n }\n if (hours.size === 0) return null\n return Math.max(...hours)\n }\n\n /** The whole timeline as a backup file body (Settings > Memory, session.memory.exportFile). */\n async exportBackup(): Promise<MemoryBackup> {\n await this.ensureLoaded()\n return {\n format: \"glassly-memory\",\n version: 1,\n exportedAt: Date.now(),\n entries: [...this.entries],\n summaries: [...this.summaries].sort((a, b) => a.hourStart - b.hourStart),\n }\n }\n\n /**\n * Merge a backup into the timeline. Skips entries whose id is stored, notes\n * whose text is already in the same hour, and hours that already have a\n * summary. Throws MemoryBackupError when `data` isn't a memory backup.\n */\n async importBackup(data: unknown): Promise<MemoryImportResult> {\n const backup = parseBackup(data)\n await this.ensureLoaded()\n const result: MemoryImportResult = {entries: 0, summaries: 0, skipped: 0}\n\n const ids = new Set(this.entries.map((e) => e.id))\n const noteKeys = new Set(this.entries.filter((e) => e.kind === \"note\").map(noteKey))\n for (const entry of backup.entries) {\n const key = entry.kind === \"note\" ? noteKey(entry) : null\n if (ids.has(entry.id) || (key != null && noteKeys.has(key))) {\n result.skipped++\n continue\n }\n ids.add(entry.id)\n if (key != null) noteKeys.add(key)\n this.entries.push(entry)\n result.entries++\n }\n\n const byHour = new Map(this.summaries.map((s) => [s.hourStart, s]))\n for (const summary of backup.summaries) {\n if (byHour.get(summary.hourStart)?.text.trim()) {\n result.skipped++\n continue\n }\n byHour.set(summary.hourStart, summary)\n result.summaries++\n }\n\n if (result.entries > 0) {\n this.entries.sort((a, b) => a.at - b.at)\n this.prune()\n await this.persistLog()\n }\n if (result.summaries > 0) {\n this.summaries = [...byHour.values()].sort((a, b) => a.hourStart - b.hourStart).slice(-MEMORY_BOUNDS.maxSummaries)\n await this.persistSummaries()\n }\n if (result.entries > 0 || result.summaries > 0) this.emitChanged()\n return result\n }\n\n /** Plain-text export of a scope, for the OS share sheet. */\n async exportText(scope: \"session\" | \"day\" | \"all\", at?: number): Promise<string> {\n const sessions = (await this.sessionList()).filter((s) => {\n if (scope === \"all\") return true\n if (at == null) return false\n if (scope === \"day\") return dayStartOf(s.hourStart) === dayStartOf(at)\n return s.hourStart === hourStartOf(at)\n })\n if (sessions.length === 0) return \"No session history to export.\"\n\n const dayFmt = (ms: number) =>\n new Date(ms).toLocaleDateString(undefined, {weekday: \"long\", year: \"numeric\", month: \"long\", day: \"numeric\"})\n const hourFmt = (ms: number) => new Date(ms).toLocaleTimeString(undefined, {hour: \"numeric\"})\n const timeFmt = (ms: number) => new Date(ms).toLocaleTimeString(undefined, {hour: \"numeric\", minute: \"2-digit\"})\n\n const parts: string[] = []\n let lastDay: number | null = null\n for (const s of [...sessions].sort((a, b) => a.hourStart - b.hourStart)) {\n const day = dayStartOf(s.hourStart)\n if (day !== lastDay) {\n parts.push(`\\n=== ${dayFmt(day)} ===`)\n lastDay = day\n }\n parts.push(`\\n[${hourFmt(s.hourStart)}–${hourFmt(s.hourStart + HOUR_MS)}]`)\n if (s.summary) parts.push(`Summary: ${s.summary}`)\n for (const e of s.entries) {\n const who = e.kind === \"note\" ? \"NOTE\" : (e.speakerId ?? \"—\")\n parts.push(` ${timeFmt(e.at)} ${who}: ${e.text}`)\n }\n }\n return `Memory session history\\n${parts.join(\"\\n\").trim()}\\n`\n }\n\n // === Load / persist ===\n\n private async ensureLoaded(): Promise<void> {\n const userId = (await this.hooks.getUserId()).trim()\n if (!userId) throw new Error(\"Glassly user identity is unavailable\")\n if (this.loadedUserId === userId) return\n if (!this.loadPromise) {\n this.loadPromise = this.loadFor(userId).finally(() => {\n this.loadPromise = null\n })\n }\n await this.loadPromise\n }\n\n private async loadFor(userId: string): Promise<void> {\n // Don't let an identity switch discard lines still sitting in the debounce\n // window — they belong to the OUTGOING user, whose key is still loaded.\n await this.flush()\n this.entries = []\n this.summaries = []\n const rawLog = this.hooks.backend.get(this.key(userId, LOG_SUFFIX))\n if (rawLog != null) {\n this.entries = parseArray(rawLog).filter(isEntry)\n } else {\n this.migrateFromAssist(userId)\n }\n const rawSummaries = this.hooks.backend.get(this.key(userId, SUMMARY_SUFFIX))\n if (rawSummaries != null) {\n this.summaries = parseArray(rawSummaries).filter(isSummary)\n }\n this.loadedUserId = userId\n this.prune()\n }\n\n /**\n * One-time migration: fold the Assist miniapp's per-app store (the previous\n * home of this timeline) into the shared user-scoped keys. Assist's old keys\n * are left in place; its new build reads through the memory bridge instead.\n */\n private migrateFromAssist(userId: string): void {\n const legacyPrefix = `${LEGACY_STORAGE_ROOT}${userId}_${LEGACY_ASSIST_PACKAGE}_`\n try {\n const migrated: MemoryEntry[] = []\n const rawLog = this.hooks.backend.get(legacyPrefix + LEGACY_LOG_KEY)\n if (rawLog != null) {\n migrated.push(...parseArray(rawLog).filter(isEntry))\n } else {\n // Pre-unification Assist: separate transcript log + notes stores.\n for (const v of parseArray(this.hooks.backend.get(legacyPrefix + LEGACY_TRANSCRIPT_KEY))) {\n const e = v as Record<string, unknown>\n if (typeof e.text === \"string\" && typeof e.at === \"number\") {\n migrated.push({\n id: makeId(),\n kind: \"line\",\n text: e.text,\n at: e.at,\n speakerId: typeof e.speakerId === \"string\" ? e.speakerId : null,\n language: typeof e.language === \"string\" ? e.language : null,\n })\n }\n }\n for (const v of parseArray(this.hooks.backend.get(legacyPrefix + LEGACY_NOTES_KEY))) {\n const n = v as Record<string, unknown>\n if (typeof n.text === \"string\" && typeof n.createdAt === \"number\") {\n migrated.push({\n id: makeId(),\n kind: \"note\",\n text: n.text,\n at: n.createdAt,\n speakerId: null,\n language: null,\n source: n.source === \"voice\" || n.source === \"auto\" ? n.source : \"ui\",\n })\n }\n }\n }\n migrated.sort((a, b) => a.at - b.at)\n this.entries = migrated\n if (migrated.length > 0) this.hooks.backend.set(this.key(userId, LOG_SUFFIX), JSON.stringify(migrated))\n\n const rawSummaries = this.hooks.backend.get(legacyPrefix + LEGACY_SUMMARY_KEY)\n if (rawSummaries != null) {\n const summaries = parseArray(rawSummaries).filter(isSummary)\n this.summaries = summaries\n if (summaries.length > 0) this.hooks.backend.set(this.key(userId, SUMMARY_SUFFIX), JSON.stringify(summaries))\n }\n if (this.entries.length > 0 || this.summaries.length > 0) {\n console.log(\n `MemoryStore: migrated ${this.entries.length} entries + ${this.summaries.length} summaries from Assist`,\n )\n }\n } catch (err) {\n console.warn(\"MemoryStore: Assist migration failed:\", err instanceof Error ? err.message : err)\n }\n }\n\n /** Keep the newest MEMORY_BOUNDS.maxEntries; nothing goes for age — deleting is the user's call. */\n private prune(): void {\n const {maxEntries} = MEMORY_BOUNDS\n if (this.entries.length > maxEntries) this.entries = this.entries.slice(-maxEntries)\n }\n\n private key(userId: string, suffix: string): string {\n return `${KEY_ROOT}${userId}_${suffix}`\n }\n\n /**\n * Mark the log dirty and arm the debounce timer. Repeated appends inside the\n * window collapse into a single serialization; the first append after a\n * flush starts a fresh window (trailing-edge, so a steady stream of speech\n * still lands on disk every `writeDebounceMs`).\n */\n private schedulePersistLog(): void {\n const delay = this.hooks.writeDebounceMs ?? DEFAULT_WRITE_DEBOUNCE_MS\n if (delay <= 0) {\n void this.persistLog()\n return\n }\n this.logDirty = true\n if (this.logFlushTimer) return\n this.logFlushTimer = BgTimer.setTimeout(() => {\n this.logFlushTimer = null\n void this.persistLog().catch((err) =>\n console.warn(\"MemoryStore: debounced log write failed:\", err instanceof Error ? err.message : err),\n )\n }, delay)\n }\n\n private async persistLog(): Promise<void> {\n // Any write of the full array satisfies whatever the debounce was holding.\n this.logDirty = false\n if (this.logFlushTimer) {\n BgTimer.clearTimeout(this.logFlushTimer)\n this.logFlushTimer = null\n }\n if (this.loadedUserId == null) return\n this.hooks.backend.set(this.key(this.loadedUserId, LOG_SUFFIX), JSON.stringify(this.entries))\n }\n\n private async persistSummaries(): Promise<void> {\n if (this.loadedUserId == null) return\n this.hooks.backend.set(this.key(this.loadedUserId, SUMMARY_SUFFIX), JSON.stringify(this.summaries))\n }\n\n private emitChanged(): void {\n for (const listener of this.changeListeners) {\n try {\n listener()\n } catch (err) {\n console.warn(\"MemoryStore: onChanged listener threw:\", err instanceof Error ? err.message : err)\n }\n }\n }\n}\n\n/** Stored values may be a JSON string (SimpleStorage writes) or a raw array. */\nfunction parseArray(raw: unknown): unknown[] {\n try {\n if (typeof raw === \"string\") {\n const parsed = JSON.parse(raw) as unknown\n return Array.isArray(parsed) ? parsed : []\n }\n return Array.isArray(raw) ? raw : []\n } catch {\n return []\n }\n}\n\nfunction normalize(text: string): string {\n return text\n .toLowerCase()\n .replace(/[^\\p{L}\\p{N}]+/gu, \" \")\n .trim()\n}\n\nfunction noteKey(entry: MemoryEntry): string {\n return `${hourStartOf(entry.at)}:${normalize(entry.text)}`\n}\n\nfunction parseBackup(data: unknown): {entries: MemoryEntry[]; summaries: MemorySummary[]} {\n const raw = data as Record<string, unknown> | null\n if (!raw || typeof raw !== \"object\" || raw.format !== \"glassly-memory\" || !Array.isArray(raw.entries)) {\n throw new MemoryBackupError(\"Not a Glassly memory backup\")\n }\n if (typeof raw.version !== \"number\" || raw.version > 1) {\n throw new MemoryBackupError(`Unsupported memory backup version ${String(raw.version)}`)\n }\n const entries: MemoryEntry[] = []\n for (const v of raw.entries) {\n if (!isEntry(v) || !v.id || !v.text.trim() || !Number.isFinite(v.at)) continue\n entries.push({\n id: v.id,\n kind: v.kind,\n text: v.text.trim(),\n at: v.at,\n speakerId: typeof v.speakerId === \"string\" ? v.speakerId : null,\n ...(typeof v.resolvedSpeakerName === \"string\" && v.resolvedSpeakerName\n ? {resolvedSpeakerName: v.resolvedSpeakerName}\n : {}),\n language: typeof v.language === \"string\" ? v.language : null,\n ...(v.kind === \"note\" ? {source: v.source === \"voice\" || v.source === \"auto\" ? v.source : \"ui\"} : {}),\n })\n }\n const summaries = (Array.isArray(raw.summaries) ? raw.summaries : [])\n .filter(isSummary)\n .filter((s) => s.text.trim() && Number.isFinite(s.hourStart))\n .map((s) => ({hourStart: s.hourStart, text: s.text, createdAt: s.createdAt}))\n return {entries, summaries}\n}\n\nfunction isEntry(v: unknown): v is MemoryEntry {\n if (!v || typeof v !== \"object\") return false\n const e = v as Record<string, unknown>\n return (\n typeof e.id === \"string\" &&\n (e.kind === \"line\" || e.kind === \"note\") &&\n typeof e.text === \"string\" &&\n typeof e.at === \"number\"\n )\n}\n\nfunction isSummary(v: unknown): v is MemorySummary {\n if (!v || typeof v !== \"object\") return false\n const s = v as Record<string, unknown>\n return typeof s.hourStart === \"number\" && typeof s.text === \"string\" && typeof s.createdAt === \"number\"\n}\n"]}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Memory backup files: the share-sheet export and file-picker import behind
|
|
3
|
+
* Settings > Memory and session.memory.exportFile/importFile.
|
|
4
|
+
*/
|
|
5
|
+
import type { MemoryExportResult, MemoryImportResult } from "@glassly/miniapp";
|
|
6
|
+
export declare function exportMemoryFile(): Promise<MemoryExportResult>;
|
|
7
|
+
/** Resolves null when the user cancels the picker. */
|
|
8
|
+
export declare function importMemoryFile(): Promise<MemoryImportResult | null>;
|
|
9
|
+
//# sourceMappingURL=memoryBackupFile.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memoryBackupFile.d.ts","sourceRoot":"","sources":["../../src/services/memoryBackupFile.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAC,kBAAkB,EAAE,kBAAkB,EAAC,MAAM,kBAAkB,CAAA;AAY5E,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,kBAAkB,CAAC,CAapE;AAED,sDAAsD;AACtD,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAU3E"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Memory backup files: the share-sheet export and file-picker import behind
|
|
3
|
+
* Settings > Memory and session.memory.exportFile/importFile.
|
|
4
|
+
*/
|
|
5
|
+
import { File, Paths } from "expo-file-system";
|
|
6
|
+
import { Platform } from "react-native";
|
|
7
|
+
import Share from "react-native-share";
|
|
8
|
+
import { MemoryBackupError } from "./MemoryStore";
|
|
9
|
+
import { memoryStore } from "./memoryStoreInstance";
|
|
10
|
+
// Some Android document providers report .json files as octet-stream.
|
|
11
|
+
const PICK_MIME_TYPES = Platform.OS === "android" ? ["application/json", "application/octet-stream"] : ["application/json"];
|
|
12
|
+
export async function exportMemoryFile() {
|
|
13
|
+
const backup = await memoryStore.exportBackup();
|
|
14
|
+
const filename = `glassly-memory-${localDate(backup.exportedAt)}.json`;
|
|
15
|
+
const file = new File(Paths.cache, filename);
|
|
16
|
+
file.writeSync(JSON.stringify(backup));
|
|
17
|
+
try {
|
|
18
|
+
await Share.open({ url: file.uri, type: "application/json", filename, title: "Glassly memory" });
|
|
19
|
+
return { success: true };
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
// react-native-share rejects when the user dismisses the sheet.
|
|
23
|
+
if (error?.message?.includes("User did not share"))
|
|
24
|
+
return { success: false, cancelled: true };
|
|
25
|
+
throw error;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/** Resolves null when the user cancels the picker. */
|
|
29
|
+
export async function importMemoryFile() {
|
|
30
|
+
const picked = await File.pickFileAsync({ mimeTypes: PICK_MIME_TYPES });
|
|
31
|
+
if (picked.canceled)
|
|
32
|
+
return null;
|
|
33
|
+
let data;
|
|
34
|
+
try {
|
|
35
|
+
data = JSON.parse(await picked.result.text());
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
throw new MemoryBackupError("Not a Glassly memory backup");
|
|
39
|
+
}
|
|
40
|
+
return memoryStore.importBackup(data);
|
|
41
|
+
}
|
|
42
|
+
function localDate(ms) {
|
|
43
|
+
const d = new Date(ms);
|
|
44
|
+
const pad = (n) => String(n).padStart(2, "0");
|
|
45
|
+
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=memoryBackupFile.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memoryBackupFile.js","sourceRoot":"","sources":["../../src/services/memoryBackupFile.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAC,IAAI,EAAE,KAAK,EAAC,MAAM,kBAAkB,CAAA;AAC5C,OAAO,EAAC,QAAQ,EAAC,MAAM,cAAc,CAAA;AACrC,OAAO,KAAK,MAAM,oBAAoB,CAAA;AAEtC,OAAO,EAAC,iBAAiB,EAAC,MAAM,eAAe,CAAA;AAC/C,OAAO,EAAC,WAAW,EAAC,MAAM,uBAAuB,CAAA;AAEjD,sEAAsE;AACtE,MAAM,eAAe,GACnB,QAAQ,CAAC,EAAE,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,kBAAkB,EAAE,0BAA0B,CAAC,CAAC,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAA;AAErG,MAAM,CAAC,KAAK,UAAU,gBAAgB;IACpC,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,YAAY,EAAE,CAAA;IAC/C,MAAM,QAAQ,GAAG,kBAAkB,SAAS,CAAC,MAAM,CAAC,UAAU,CAAC,OAAO,CAAA;IACtE,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAA;IAC5C,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAA;IACtC,IAAI,CAAC;QACH,MAAM,KAAK,CAAC,IAAI,CAAC,EAAC,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,IAAI,EAAE,kBAAkB,EAAE,QAAQ,EAAE,KAAK,EAAE,gBAAgB,EAAC,CAAC,CAAA;QAC9F,OAAO,EAAC,OAAO,EAAE,IAAI,EAAC,CAAA;IACxB,CAAC;IAAC,OAAO,KAAU,EAAE,CAAC;QACpB,gEAAgE;QAChE,IAAI,KAAK,EAAE,OAAO,EAAE,QAAQ,CAAC,oBAAoB,CAAC;YAAE,OAAO,EAAC,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAC,CAAA;QAC5F,MAAM,KAAK,CAAA;IACb,CAAC;AACH,CAAC;AAED,sDAAsD;AACtD,MAAM,CAAC,KAAK,UAAU,gBAAgB;IACpC,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC,EAAC,SAAS,EAAE,eAAe,EAAC,CAAC,CAAA;IACrE,IAAI,MAAM,CAAC,QAAQ;QAAE,OAAO,IAAI,CAAA;IAChC,IAAI,IAAa,CAAA;IACjB,IAAI,CAAC;QACH,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAA;IAC/C,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,iBAAiB,CAAC,6BAA6B,CAAC,CAAA;IAC5D,CAAC;IACD,OAAO,WAAW,CAAC,YAAY,CAAC,IAAI,CAAC,CAAA;AACvC,CAAC;AAED,SAAS,SAAS,CAAC,EAAU;IAC3B,MAAM,CAAC,GAAG,IAAI,IAAI,CAAC,EAAE,CAAC,CAAA;IACtB,MAAM,GAAG,GAAG,CAAC,CAAS,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAA;IACrD,OAAO,GAAG,CAAC,CAAC,WAAW,EAAE,IAAI,GAAG,CAAC,CAAC,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,EAAE,CAAA;AAC1E,CAAC","sourcesContent":["/**\n * Memory backup files: the share-sheet export and file-picker import behind\n * Settings > Memory and session.memory.exportFile/importFile.\n */\n\nimport type {MemoryExportResult, MemoryImportResult} from \"@glassly/miniapp\"\nimport {File, Paths} from \"expo-file-system\"\nimport {Platform} from \"react-native\"\nimport Share from \"react-native-share\"\n\nimport {MemoryBackupError} from \"./MemoryStore\"\nimport {memoryStore} from \"./memoryStoreInstance\"\n\n// Some Android document providers report .json files as octet-stream.\nconst PICK_MIME_TYPES =\n Platform.OS === \"android\" ? [\"application/json\", \"application/octet-stream\"] : [\"application/json\"]\n\nexport async function exportMemoryFile(): Promise<MemoryExportResult> {\n const backup = await memoryStore.exportBackup()\n const filename = `glassly-memory-${localDate(backup.exportedAt)}.json`\n const file = new File(Paths.cache, filename)\n file.writeSync(JSON.stringify(backup))\n try {\n await Share.open({url: file.uri, type: \"application/json\", filename, title: \"Glassly memory\"})\n return {success: true}\n } catch (error: any) {\n // react-native-share rejects when the user dismisses the sheet.\n if (error?.message?.includes(\"User did not share\")) return {success: false, cancelled: true}\n throw error\n }\n}\n\n/** Resolves null when the user cancels the picker. */\nexport async function importMemoryFile(): Promise<MemoryImportResult | null> {\n const picked = await File.pickFileAsync({mimeTypes: PICK_MIME_TYPES})\n if (picked.canceled) return null\n let data: unknown\n try {\n data = JSON.parse(await picked.result.text())\n } catch {\n throw new MemoryBackupError(\"Not a Glassly memory backup\")\n }\n return memoryStore.importBackup(data)\n}\n\nfunction localDate(ms: number): string {\n const d = new Date(ms)\n const pad = (n: number) => String(n).padStart(2, \"0\")\n return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@glassly/engine",
|
|
3
|
-
"version": "0.3.5-dev.
|
|
3
|
+
"version": "0.3.5-dev.773",
|
|
4
4
|
"description": "Glassly Engine — miniapp registry, WebView bridge, and miniapp store",
|
|
5
5
|
"main": "build/index.js",
|
|
6
6
|
"react-native": "src/index.ts",
|
|
@@ -97,17 +97,17 @@
|
|
|
97
97
|
"registry": "https://registry.npmjs.org/"
|
|
98
98
|
},
|
|
99
99
|
"dependencies": {
|
|
100
|
-
"@glassly/bluetooth-sdk": "0.3.5-dev.
|
|
101
|
-
"@glassly/cloud-client": "0.3.5-dev.
|
|
102
|
-
"@glassly/cloud-protocol": "0.3.5-dev.
|
|
103
|
-
"@glassly/crust": "0.3.5-dev.
|
|
104
|
-
"@glassly/miniapp": "0.3.5-dev.
|
|
100
|
+
"@glassly/bluetooth-sdk": "0.3.5-dev.773",
|
|
101
|
+
"@glassly/cloud-client": "0.3.5-dev.773",
|
|
102
|
+
"@glassly/cloud-protocol": "0.3.5-dev.773",
|
|
103
|
+
"@glassly/crust": "0.3.5-dev.773",
|
|
104
|
+
"@glassly/miniapp": "0.3.5-dev.773",
|
|
105
105
|
"buffer": "^6.0.3",
|
|
106
106
|
"events": "^3.3.0",
|
|
107
107
|
"react-native-marked": "8.1.1",
|
|
108
108
|
"semver": "^7.7.4",
|
|
109
109
|
"typesafe-ts": "^1.7.1",
|
|
110
|
-
"@glassly/llm-providers": "0.3.5-dev.
|
|
110
|
+
"@glassly/llm-providers": "0.3.5-dev.773",
|
|
111
111
|
"@huggingface/jinja": "^0.5.9",
|
|
112
112
|
"jsonrepair": "^3.12.0"
|
|
113
113
|
},
|
package/src/facades/memory.ts
CHANGED
|
@@ -4,9 +4,10 @@
|
|
|
4
4
|
* purge everything; the full timeline surface (lines, sessions, summaries)
|
|
5
5
|
* is what miniapps reach through session.memory with the MEMORY permission.
|
|
6
6
|
*/
|
|
7
|
-
import type {MemoryEntry, MemorySession} from "@glassly/miniapp"
|
|
7
|
+
import type {MemoryEntry, MemoryExportResult, MemoryImportResult, MemorySession} from "@glassly/miniapp"
|
|
8
8
|
|
|
9
|
-
import
|
|
9
|
+
import {exportMemoryFile, importMemoryFile} from "../services/memoryBackupFile"
|
|
10
|
+
import {MemoryBackupError, type MemoryStats} from "../services/MemoryStore"
|
|
10
11
|
import {memoryStore} from "../services/memoryStoreInstance"
|
|
11
12
|
|
|
12
13
|
export const memory = {
|
|
@@ -30,6 +31,14 @@ export const memory = {
|
|
|
30
31
|
stats: (): Promise<MemoryStats> => memoryStore.stats(),
|
|
31
32
|
/** Drop the entire timeline: every entry and every summary. */
|
|
32
33
|
clearAll: (): Promise<void> => memoryStore.clear(),
|
|
34
|
+
/** Share the whole timeline as a JSON backup file. */
|
|
35
|
+
exportFile: (): Promise<MemoryExportResult> => exportMemoryFile(),
|
|
36
|
+
/**
|
|
37
|
+
* Pick a backup file and merge it in, skipping what's already stored.
|
|
38
|
+
* Resolves null on cancel; rejects with a MemoryBackupError for a non-backup file.
|
|
39
|
+
*/
|
|
40
|
+
importFile: (): Promise<MemoryImportResult | null> => importMemoryFile(),
|
|
41
|
+
isBackupError: (err: unknown): boolean => err instanceof MemoryBackupError,
|
|
33
42
|
/** Subscribe to store mutations (from miniapps or Settings). */
|
|
34
43
|
onChanged: (listener: () => void): (() => void) => memoryStore.onChanged(listener),
|
|
35
44
|
}
|
|
@@ -12,9 +12,9 @@ export interface EngineReleaseMetadata {
|
|
|
12
12
|
export const ENGINE_RELEASE_METADATA: Readonly<EngineReleaseMetadata> = Object.freeze({
|
|
13
13
|
"schemaVersion": 1,
|
|
14
14
|
"familyBaseVersion": "0.3.5",
|
|
15
|
-
"releaseIdentity": "0.3.5-dev.
|
|
16
|
-
"releaseSetId": "glassly-0.3.5-dev.
|
|
17
|
-
"sourceCommit": "
|
|
15
|
+
"releaseIdentity": "0.3.5-dev.773",
|
|
16
|
+
"releaseSetId": "glassly-0.3.5-dev.773",
|
|
17
|
+
"sourceCommit": "c48886d82edd42a7d012af3b8936251ac92ba949",
|
|
18
18
|
"otaManifestUrl": null,
|
|
19
19
|
"otaManifestSha256": null
|
|
20
20
|
})
|
|
@@ -184,6 +184,8 @@ import {listPhoneCalendarEvents, PhoneCalendarError} from "./PhoneCalendarServic
|
|
|
184
184
|
import {LocalMiniappStorage} from "./LocalMiniappStorage"
|
|
185
185
|
import {captureDecisionFor, type CaptureCandidate} from "./MemoryCapture"
|
|
186
186
|
import {memoryStore} from "./memoryStoreInstance"
|
|
187
|
+
import {exportMemoryFile, importMemoryFile} from "./memoryBackupFile"
|
|
188
|
+
import {MemoryBackupError} from "./MemoryStore"
|
|
187
189
|
import {speakerProfileStore} from "./speakerProfileStoreInstance"
|
|
188
190
|
|
|
189
191
|
// =============================================================================
|
|
@@ -1592,6 +1594,8 @@ class LocalMiniappRuntime {
|
|
|
1592
1594
|
case MiniappRequestType.MEMORY_QUERY:
|
|
1593
1595
|
case MiniappRequestType.MEMORY_WINDOW:
|
|
1594
1596
|
case MiniappRequestType.MEMORY_EXPORT_TEXT:
|
|
1597
|
+
case MiniappRequestType.MEMORY_EXPORT_FILE:
|
|
1598
|
+
case MiniappRequestType.MEMORY_IMPORT_FILE:
|
|
1595
1599
|
case MiniappRequestType.MEMORY_SUMMARY_ADD:
|
|
1596
1600
|
case MiniappRequestType.MEMORY_SUMMARY_LIST:
|
|
1597
1601
|
case MiniappRequestType.MEMORY_PENDING_SUMMARY_HOUR:
|
|
@@ -4007,6 +4011,14 @@ class LocalMiniappRuntime {
|
|
|
4007
4011
|
this.sendResult(packageName, requestId, true, {text: await memoryStore.exportText(scope, num(payload.at))})
|
|
4008
4012
|
return
|
|
4009
4013
|
}
|
|
4014
|
+
case MiniappRequestType.MEMORY_EXPORT_FILE: {
|
|
4015
|
+
this.sendResult(packageName, requestId, true, await exportMemoryFile())
|
|
4016
|
+
return
|
|
4017
|
+
}
|
|
4018
|
+
case MiniappRequestType.MEMORY_IMPORT_FILE: {
|
|
4019
|
+
this.sendResult(packageName, requestId, true, {imported: await importMemoryFile()})
|
|
4020
|
+
return
|
|
4021
|
+
}
|
|
4010
4022
|
case MiniappRequestType.MEMORY_SUMMARY_ADD: {
|
|
4011
4023
|
const hourStart = num(payload.hourStart)
|
|
4012
4024
|
if (hourStart == null || typeof payload.text !== "string") {
|
|
@@ -4042,6 +4054,13 @@ class LocalMiniappRuntime {
|
|
|
4042
4054
|
})
|
|
4043
4055
|
}
|
|
4044
4056
|
} catch (err) {
|
|
4057
|
+
if (err instanceof MemoryBackupError) {
|
|
4058
|
+
this.sendResult(packageName, requestId, false, undefined, {
|
|
4059
|
+
code: MiniappErrorCode.INVALID_ARGUMENT,
|
|
4060
|
+
message: err.message,
|
|
4061
|
+
})
|
|
4062
|
+
return
|
|
4063
|
+
}
|
|
4045
4064
|
console.error(`${LOG_TAG}: ${requestType} error:`, err)
|
|
4046
4065
|
this.sendResult(packageName, requestId, false, undefined, {
|
|
4047
4066
|
code: MiniappErrorCode.INTERNAL,
|