@smyslenny/agent-memory 4.0.0-alpha.1 → 4.1.0-alpha.1

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/index.d.ts CHANGED
@@ -38,6 +38,7 @@ interface Memory {
38
38
  source: string | null;
39
39
  agent_id: string;
40
40
  hash: string | null;
41
+ emotion_tag: string | null;
41
42
  }
42
43
  interface CreateMemoryInput {
43
44
  content: string;
@@ -47,6 +48,7 @@ interface CreateMemoryInput {
47
48
  source?: string;
48
49
  agent_id?: string;
49
50
  embedding_provider_id?: string | null;
51
+ emotion_tag?: string;
50
52
  }
51
53
  interface UpdateMemoryInput {
52
54
  content?: string;
@@ -57,6 +59,7 @@ interface UpdateMemoryInput {
57
59
  stability?: number;
58
60
  source?: string;
59
61
  embedding_provider_id?: string | null;
62
+ emotion_tag?: string | null;
60
63
  }
61
64
  declare function contentHash(content: string): string;
62
65
  declare function createMemory(db: Database.Database, input: CreateMemoryInput): Memory | null;
@@ -70,6 +73,7 @@ declare function listMemories(db: Database.Database, opts?: {
70
73
  min_vitality?: number;
71
74
  limit?: number;
72
75
  offset?: number;
76
+ emotion_tag?: string;
73
77
  }): Memory[];
74
78
  declare function recordAccess(db: Database.Database, id: string, growthFactor?: number): void;
75
79
  declare function countMemories(db: Database.Database, agent_id?: string): {
@@ -88,6 +92,7 @@ interface SyncInput {
88
92
  agent_id?: string;
89
93
  provider?: EmbeddingProvider | null;
90
94
  conservative?: boolean;
95
+ emotion_tag?: string;
91
96
  }
92
97
  interface SyncResult {
93
98
  action: "added" | "updated" | "merged" | "skipped";
@@ -115,6 +120,7 @@ interface RememberInput {
115
120
  agent_id?: string;
116
121
  provider?: EmbeddingProvider | null;
117
122
  conservative?: boolean;
123
+ emotion_tag?: string;
118
124
  }
119
125
  declare function rememberMemory(db: Database.Database, input: RememberInput): Promise<SyncResult>;
120
126
 
@@ -258,6 +264,7 @@ interface RecallInput {
258
264
  vectorLimit?: number;
259
265
  provider?: EmbeddingProvider | null;
260
266
  recordAccess?: boolean;
267
+ emotion_tag?: string;
261
268
  }
262
269
  declare function recallMemory(db: Database.Database, input: RecallInput): Promise<HybridRecallResponse>;
263
270
 
@@ -294,6 +301,7 @@ interface SurfaceInput {
294
301
  agent_id?: string;
295
302
  provider?: EmbeddingProvider | null;
296
303
  min_vitality?: number;
304
+ emotion_tag?: string;
297
305
  }
298
306
  interface SurfaceResult {
299
307
  memory: Memory;
@@ -713,13 +721,47 @@ interface BootResult {
713
721
  identityMemories: Memory[];
714
722
  bootPaths: string[];
715
723
  }
724
+ interface WarmBootOptions {
725
+ agent_id?: string;
726
+ corePaths?: string[];
727
+ format?: "json" | "narrative";
728
+ agent_name?: string;
729
+ }
730
+ interface WarmBootResult extends BootResult {
731
+ narrative?: string;
732
+ layers?: {
733
+ identity: Memory[];
734
+ emotion: Memory[];
735
+ event: Memory[];
736
+ knowledge: Memory[];
737
+ };
738
+ }
716
739
  /**
717
- * Load core identity memories at startup.
740
+ * Format a relative time string from an ISO date.
741
+ */
742
+ declare function formatRelativeDate(isoDate: string): string;
743
+ /**
744
+ * Load layered memories for warm boot.
745
+ */
746
+ declare function loadWarmBootLayers(db: Database.Database, agentId: string): {
747
+ identity: Memory[];
748
+ emotion: Memory[];
749
+ event: Memory[];
750
+ knowledge: Memory[];
751
+ };
752
+ /**
753
+ * Format layered memories as narrative Markdown.
754
+ */
755
+ declare function formatNarrativeBoot(layers: {
756
+ identity: Memory[];
757
+ emotion: Memory[];
758
+ event: Memory[];
759
+ knowledge: Memory[];
760
+ }, agentName: string): string;
761
+ /**
762
+ * Load core identity memories at startup (legacy JSON format).
718
763
  * Returns all P0 (identity) memories + any memories referenced by system://boot.
719
764
  */
720
- declare function boot(db: Database.Database, opts?: {
721
- agent_id?: string;
722
- corePaths?: string[];
723
- }): BootResult;
765
+ declare function boot(db: Database.Database, opts?: WarmBootOptions): WarmBootResult;
724
766
 
725
- export { type AgentMemoryHttpServer, type ReflectInput as AppReflectInput, type ReflectProgressEvent as AppReflectProgressEvent, type AutoIngestWatcher, type AutoIngestWatcherOptions, type BootResult, type CreateMemoryInput, type DedupScoreBreakdown, type EmbeddingProvider, type EmbeddingProviderConfig, type EmbeddingProviderKind, type EmbeddingProviderOptions, type EmbeddingStatus, type EvictionCandidate, type ExportResult, type FeedbackEventInput, type FeedbackEventRecord, type FeedbackSource, type FeedbackSummary, type GovernResult, type GuardAction, type GuardInput, type GuardResult, type HttpJobStatus, type HttpServerOptions, type HybridRecallResponse, type HybridRecallResult, type IngestExtractedItem, type IngestResult, type IngestRunOptions, type MaintenanceJob, type MaintenancePhase, type MaintenanceStatus, type Memory, type MemoryType, type MergeContext, type MergePlan, type Path, type PendingEmbeddingRecord, type Priority, type RecallInput, type ReflectCheckpoint, type ReflectOptions, type ReflectProgressEvent$1 as ReflectProgressEvent, type ReflectRunResult, type ReflectRunners, type ReflectStats, type ReflectStep, type ReindexEmbeddingsResult, type ReindexInput, type ReindexProgressEvent, type ReindexSearchResult, type RememberInput, type SearchResult, type StatusResult, type StoredEmbedding, type SurfaceInput, type SurfaceIntent, type SurfaceResponse, type SurfaceResult, type SyncInput, type SyncResult, type TidyResult, type UpdateMemoryInput, type VectorSearchResult, boot, buildFtsQuery, buildMergePlan, calculateVitality, classifyIngestType, completeMaintenanceJob, computeEvictionScore, contentHash, cosineSimilarity, countMemories, createEmbeddingProvider, createHttpServer, createInitialCheckpoint, createLocalHttpEmbeddingProvider, createMaintenanceJob, createMemory, createOpenAICompatibleEmbeddingProvider, createPath, decodeVector, deleteMemory, deletePath, encodeVector, exportMemories, extractIngestItems, failMaintenanceJob, findResumableMaintenanceJob, fuseHybridResults, fusionScore, getConfiguredEmbeddingProviderId, getDecayedMemories, getEmbedding, getEmbeddingProvider, getEmbeddingProviderConfigFromEnv, getEmbeddingProviderFromEnv, getFeedbackScore, getFeedbackSummary, getMaintenanceJob, getMemory, getMemoryStatus, getPath, getPathByUri, getPathsByDomain, getPathsByMemory, getPathsByPrefix, guard, healthcheckEmbeddingProvider, ingestText, listMemories, listPendingEmbeddings, markAllEmbeddingsPending, markEmbeddingFailed, markMemoryEmbeddingPending, parseUri, priorityPrior, rankEvictionCandidates, rebuildBm25Index, recallMemories, recallMemory, recordAccess, recordFeedbackEvent, reflectMemories, reindexEmbeddings, reindexMemories, reindexMemorySearch, rememberMemory, runAutoIngestWatcher, runDecay, runGovern, runReflectOrchestrator, runTidy, searchBM25, searchByVector, slugify, splitIngestBlocks, startHttpServer, surfaceMemories, syncBatch, syncOne, tokenize, updateMaintenanceCheckpoint, updateMemory, upsertReadyEmbedding };
767
+ export { type AgentMemoryHttpServer, type ReflectInput as AppReflectInput, type ReflectProgressEvent as AppReflectProgressEvent, type AutoIngestWatcher, type AutoIngestWatcherOptions, type BootResult, type CreateMemoryInput, type DedupScoreBreakdown, type EmbeddingProvider, type EmbeddingProviderConfig, type EmbeddingProviderKind, type EmbeddingProviderOptions, type EmbeddingStatus, type EvictionCandidate, type ExportResult, type FeedbackEventInput, type FeedbackEventRecord, type FeedbackSource, type FeedbackSummary, type GovernResult, type GuardAction, type GuardInput, type GuardResult, type HttpJobStatus, type HttpServerOptions, type HybridRecallResponse, type HybridRecallResult, type IngestExtractedItem, type IngestResult, type IngestRunOptions, type MaintenanceJob, type MaintenancePhase, type MaintenanceStatus, type Memory, type MemoryType, type MergeContext, type MergePlan, type Path, type PendingEmbeddingRecord, type Priority, type RecallInput, type ReflectCheckpoint, type ReflectOptions, type ReflectProgressEvent$1 as ReflectProgressEvent, type ReflectRunResult, type ReflectRunners, type ReflectStats, type ReflectStep, type ReindexEmbeddingsResult, type ReindexInput, type ReindexProgressEvent, type ReindexSearchResult, type RememberInput, type SearchResult, type StatusResult, type StoredEmbedding, type SurfaceInput, type SurfaceIntent, type SurfaceResponse, type SurfaceResult, type SyncInput, type SyncResult, type TidyResult, type UpdateMemoryInput, type VectorSearchResult, type WarmBootOptions, type WarmBootResult, boot, buildFtsQuery, buildMergePlan, calculateVitality, classifyIngestType, completeMaintenanceJob, computeEvictionScore, contentHash, cosineSimilarity, countMemories, createEmbeddingProvider, createHttpServer, createInitialCheckpoint, createLocalHttpEmbeddingProvider, createMaintenanceJob, createMemory, createOpenAICompatibleEmbeddingProvider, createPath, decodeVector, deleteMemory, deletePath, encodeVector, exportMemories, extractIngestItems, failMaintenanceJob, findResumableMaintenanceJob, formatNarrativeBoot, formatRelativeDate, fuseHybridResults, fusionScore, getConfiguredEmbeddingProviderId, getDecayedMemories, getEmbedding, getEmbeddingProvider, getEmbeddingProviderConfigFromEnv, getEmbeddingProviderFromEnv, getFeedbackScore, getFeedbackSummary, getMaintenanceJob, getMemory, getMemoryStatus, getPath, getPathByUri, getPathsByDomain, getPathsByMemory, getPathsByPrefix, guard, healthcheckEmbeddingProvider, ingestText, listMemories, listPendingEmbeddings, loadWarmBootLayers, markAllEmbeddingsPending, markEmbeddingFailed, markMemoryEmbeddingPending, parseUri, priorityPrior, rankEvictionCandidates, rebuildBm25Index, recallMemories, recallMemory, recordAccess, recordFeedbackEvent, reflectMemories, reindexEmbeddings, reindexMemories, reindexMemorySearch, rememberMemory, runAutoIngestWatcher, runDecay, runGovern, runReflectOrchestrator, runTidy, searchBM25, searchByVector, slugify, splitIngestBlocks, startHttpServer, surfaceMemories, syncBatch, syncOne, tokenize, updateMaintenanceCheckpoint, updateMemory, upsertReadyEmbedding };
package/dist/index.js CHANGED
@@ -6,7 +6,7 @@ import { createHash as createHash2 } from "crypto";
6
6
  // src/core/db.ts
7
7
  import Database from "better-sqlite3";
8
8
  import { randomUUID } from "crypto";
9
- var SCHEMA_VERSION = 5;
9
+ var SCHEMA_VERSION = 6;
10
10
  var SCHEMA_SQL = `
11
11
  -- Memory entries
12
12
  CREATE TABLE IF NOT EXISTS memories (
@@ -24,6 +24,7 @@ CREATE TABLE IF NOT EXISTS memories (
24
24
  source TEXT,
25
25
  agent_id TEXT NOT NULL DEFAULT 'default',
26
26
  hash TEXT,
27
+ emotion_tag TEXT,
27
28
  UNIQUE(hash, agent_id)
28
29
  );
29
30
 
@@ -199,6 +200,11 @@ function migrateDatabase(db, from, to) {
199
200
  v = 5;
200
201
  continue;
201
202
  }
203
+ if (v === 5) {
204
+ migrateV5ToV6(db);
205
+ v = 6;
206
+ continue;
207
+ }
202
208
  throw new Error(`Unsupported schema migration path: v${from} \u2192 v${to} (stuck at v${v})`);
203
209
  }
204
210
  }
@@ -284,6 +290,8 @@ function inferSchemaVersion(db) {
284
290
  const hasV4Embeddings = hasEmbeddings && tableHasColumn(db, "embeddings", "provider_id") && tableHasColumn(db, "embeddings", "status") && tableHasColumn(db, "embeddings", "content_hash") && tableHasColumn(db, "embeddings", "id");
285
291
  const hasMaintenanceJobs = tableExists(db, "maintenance_jobs");
286
292
  const hasFeedbackEvents = tableExists(db, "feedback_events");
293
+ const hasEmotionTag = tableHasColumn(db, "memories", "emotion_tag");
294
+ if (hasAgentScopedPaths && hasAgentScopedLinks && hasV4Embeddings && hasMaintenanceJobs && hasFeedbackEvents && hasEmotionTag) return 6;
287
295
  if (hasAgentScopedPaths && hasAgentScopedLinks && hasV4Embeddings && hasMaintenanceJobs && hasFeedbackEvents) return 5;
288
296
  if (hasAgentScopedPaths && hasAgentScopedLinks && hasV4Embeddings) return 4;
289
297
  if (hasAgentScopedPaths && hasAgentScopedLinks && hasEmbeddings) return 3;
@@ -310,6 +318,9 @@ function ensureIndexes(db) {
310
318
  if (tableExists(db, "maintenance_jobs")) {
311
319
  db.exec("CREATE INDEX IF NOT EXISTS idx_maintenance_jobs_phase_status ON maintenance_jobs(phase, status, started_at DESC);");
312
320
  }
321
+ if (tableHasColumn(db, "memories", "emotion_tag")) {
322
+ db.exec("CREATE INDEX IF NOT EXISTS idx_memories_emotion_tag ON memories(emotion_tag) WHERE emotion_tag IS NOT NULL;");
323
+ }
313
324
  if (tableExists(db, "feedback_events")) {
314
325
  db.exec("CREATE INDEX IF NOT EXISTS idx_feedback_events_memory ON feedback_events(memory_id, created_at DESC);");
315
326
  if (tableHasColumn(db, "feedback_events", "agent_id") && tableHasColumn(db, "feedback_events", "source")) {
@@ -443,6 +454,25 @@ function migrateV4ToV5(db) {
443
454
  throw e;
444
455
  }
445
456
  }
457
+ function migrateV5ToV6(db) {
458
+ if (tableHasColumn(db, "memories", "emotion_tag")) {
459
+ db.prepare("INSERT OR REPLACE INTO schema_meta (key, value) VALUES ('version', ?)").run(String(6));
460
+ return;
461
+ }
462
+ try {
463
+ db.exec("BEGIN");
464
+ db.exec("ALTER TABLE memories ADD COLUMN emotion_tag TEXT;");
465
+ db.exec("CREATE INDEX IF NOT EXISTS idx_memories_emotion_tag ON memories(emotion_tag) WHERE emotion_tag IS NOT NULL;");
466
+ db.prepare("INSERT OR REPLACE INTO schema_meta (key, value) VALUES ('version', ?)").run(String(6));
467
+ db.exec("COMMIT");
468
+ } catch (e) {
469
+ try {
470
+ db.exec("ROLLBACK");
471
+ } catch {
472
+ }
473
+ throw e;
474
+ }
475
+ }
446
476
 
447
477
  // src/search/tokenizer.ts
448
478
  import { readFileSync } from "fs";
@@ -942,8 +972,8 @@ function createMemory(db, input) {
942
972
  const timestamp = now();
943
973
  db.prepare(
944
974
  `INSERT INTO memories (id, content, type, priority, emotion_val, vitality, stability,
945
- access_count, created_at, updated_at, source, agent_id, hash)
946
- VALUES (?, ?, ?, ?, ?, 1.0, ?, 0, ?, ?, ?, ?, ?)`
975
+ access_count, created_at, updated_at, source, agent_id, hash, emotion_tag)
976
+ VALUES (?, ?, ?, ?, ?, 1.0, ?, 0, ?, ?, ?, ?, ?, ?)`
947
977
  ).run(
948
978
  id,
949
979
  input.content,
@@ -955,7 +985,8 @@ function createMemory(db, input) {
955
985
  timestamp,
956
986
  input.source ?? null,
957
987
  agentId,
958
- hash
988
+ hash,
989
+ input.emotion_tag ?? null
959
990
  );
960
991
  db.prepare("INSERT INTO memories_fts (id, content) VALUES (?, ?)").run(id, tokenizeForIndex(input.content));
961
992
  markEmbeddingDirtyIfNeeded(db, id, hash, resolveEmbeddingProviderId(input.embedding_provider_id));
@@ -999,6 +1030,10 @@ function updateMemory(db, id, input) {
999
1030
  fields.push("source = ?");
1000
1031
  values.push(input.source);
1001
1032
  }
1033
+ if (input.emotion_tag !== void 0) {
1034
+ fields.push("emotion_tag = ?");
1035
+ values.push(input.emotion_tag);
1036
+ }
1002
1037
  fields.push("updated_at = ?");
1003
1038
  values.push(now());
1004
1039
  values.push(id);
@@ -1040,6 +1075,10 @@ function listMemories(db, opts) {
1040
1075
  conditions.push("vitality >= ?");
1041
1076
  params.push(opts.min_vitality);
1042
1077
  }
1078
+ if (opts?.emotion_tag) {
1079
+ conditions.push("emotion_tag = ?");
1080
+ params.push(opts.emotion_tag);
1081
+ }
1043
1082
  const where = conditions.length ? `WHERE ${conditions.join(" AND ")}` : "";
1044
1083
  const limit = opts?.limit ?? 100;
1045
1084
  const offset = opts?.offset ?? 0;
@@ -1703,7 +1742,8 @@ async function syncOne(db, input) {
1703
1742
  agent_id: input.agent_id,
1704
1743
  uri: input.uri,
1705
1744
  provider: input.provider,
1706
- conservative: input.conservative
1745
+ conservative: input.conservative,
1746
+ emotion_tag: input.emotion_tag
1707
1747
  };
1708
1748
  const guardResult = await guard(db, memInput);
1709
1749
  switch (guardResult.action) {
@@ -1752,21 +1792,26 @@ async function rememberMemory(db, input) {
1752
1792
  source: input.source,
1753
1793
  agent_id: input.agent_id,
1754
1794
  provider: input.provider,
1755
- conservative: input.conservative
1795
+ conservative: input.conservative,
1796
+ emotion_tag: input.emotion_tag
1756
1797
  });
1757
1798
  }
1758
1799
 
1759
1800
  // src/app/recall.ts
1760
1801
  async function recallMemory(db, input) {
1761
- return recallMemories(db, input.query, {
1802
+ const result = await recallMemories(db, input.query, {
1762
1803
  agent_id: input.agent_id,
1763
- limit: input.limit,
1804
+ limit: input.emotion_tag ? (input.limit ?? 10) * 3 : input.limit,
1764
1805
  min_vitality: input.min_vitality,
1765
1806
  lexicalLimit: input.lexicalLimit,
1766
1807
  vectorLimit: input.vectorLimit,
1767
1808
  provider: input.provider,
1768
1809
  recordAccess: input.recordAccess
1769
1810
  });
1811
+ if (input.emotion_tag) {
1812
+ result.results = result.results.filter((r) => r.memory.emotion_tag === input.emotion_tag).slice(0, input.limit ?? 10);
1813
+ }
1814
+ return result;
1770
1815
  }
1771
1816
 
1772
1817
  // src/app/feedback.ts
@@ -2043,7 +2088,7 @@ async function surfaceMemories(db, input) {
2043
2088
  signals.set(memory.id, { memory });
2044
2089
  }
2045
2090
  }
2046
- const results = [...signals.values()].map((signal) => signal.memory).filter((memory) => memory.vitality >= minVitality).filter((memory) => input.types?.length ? input.types.includes(memory.type) : true).map((memory) => {
2091
+ const results = [...signals.values()].map((signal) => signal.memory).filter((memory) => memory.vitality >= minVitality).filter((memory) => input.types?.length ? input.types.includes(memory.type) : true).filter((memory) => input.emotion_tag ? memory.emotion_tag === input.emotion_tag : true).map((memory) => {
2047
2092
  const signal = signals.get(memory.id) ?? { memory };
2048
2093
  const memoryTokens = new Set(tokenize(memory.content));
2049
2094
  const lexicalOverlap = overlapScore2(memoryTokens, queryTokens);
@@ -3307,8 +3352,68 @@ function runAutoIngestWatcher(options) {
3307
3352
  }
3308
3353
 
3309
3354
  // src/sleep/boot.ts
3355
+ function formatRelativeDate(isoDate) {
3356
+ const diffMs = Date.now() - new Date(isoDate).getTime();
3357
+ const diffDays = Math.floor(diffMs / 864e5);
3358
+ if (diffDays <= 0) return "\u4ECA\u5929";
3359
+ if (diffDays === 1) return "\u6628\u5929";
3360
+ if (diffDays <= 7) return `${diffDays}\u5929\u524D`;
3361
+ return isoDate.slice(0, 10);
3362
+ }
3363
+ function loadWarmBootLayers(db, agentId) {
3364
+ const identity = listMemories(db, { agent_id: agentId, type: "identity", limit: 50 });
3365
+ const emotion = listMemories(db, { agent_id: agentId, type: "emotion", limit: 5 });
3366
+ const event = listMemories(db, { agent_id: agentId, type: "event", limit: 7 });
3367
+ const knowledge = listMemories(db, {
3368
+ agent_id: agentId,
3369
+ type: "knowledge",
3370
+ min_vitality: 0.5,
3371
+ limit: 10
3372
+ });
3373
+ return { identity, emotion, event, knowledge };
3374
+ }
3375
+ function formatNarrativeBoot(layers, agentName) {
3376
+ const lines = [];
3377
+ lines.push(`# ${agentName}\u7684\u56DE\u5FC6`);
3378
+ lines.push("");
3379
+ if (layers.identity.length > 0) {
3380
+ lines.push("## \u6211\u662F\u8C01");
3381
+ for (const mem of layers.identity) {
3382
+ lines.push(`- ${mem.content.split("\n")[0].slice(0, 200)}`);
3383
+ }
3384
+ lines.push("");
3385
+ }
3386
+ if (layers.emotion.length > 0) {
3387
+ lines.push("## \u6700\u8FD1\u7684\u5FC3\u60C5");
3388
+ for (const mem of layers.emotion) {
3389
+ const tag = mem.emotion_tag;
3390
+ const time = formatRelativeDate(mem.updated_at);
3391
+ const tagStr = tag ? `${tag}, ${time}` : time;
3392
+ lines.push(`- ${mem.content.split("\n")[0].slice(0, 200)} (${tagStr})`);
3393
+ }
3394
+ lines.push("");
3395
+ }
3396
+ if (layers.event.length > 0) {
3397
+ lines.push("## \u6700\u8FD1\u53D1\u751F\u7684\u4E8B");
3398
+ for (const mem of layers.event) {
3399
+ const time = formatRelativeDate(mem.updated_at);
3400
+ lines.push(`- ${mem.content.split("\n")[0].slice(0, 200)} (${time})`);
3401
+ }
3402
+ lines.push("");
3403
+ }
3404
+ if (layers.knowledge.length > 0) {
3405
+ lines.push("## \u8FD8\u8BB0\u5F97\u7684\u77E5\u8BC6");
3406
+ for (const mem of layers.knowledge) {
3407
+ lines.push(`- ${mem.content.split("\n")[0].slice(0, 200)}`);
3408
+ }
3409
+ lines.push("");
3410
+ }
3411
+ return lines.join("\n");
3412
+ }
3310
3413
  function boot(db, opts) {
3311
3414
  const agentId = opts?.agent_id ?? "default";
3415
+ const format = opts?.format ?? "json";
3416
+ const agentName = opts?.agent_name ?? "Agent";
3312
3417
  const corePaths = opts?.corePaths ?? [
3313
3418
  "core://agent",
3314
3419
  "core://user",
@@ -3352,10 +3457,16 @@ function boot(db, opts) {
3352
3457
  }
3353
3458
  }
3354
3459
  }
3355
- return {
3460
+ const result = {
3356
3461
  identityMemories: [...memories.values()],
3357
3462
  bootPaths
3358
3463
  };
3464
+ if (format === "narrative") {
3465
+ const layers = loadWarmBootLayers(db, agentId);
3466
+ result.layers = layers;
3467
+ result.narrative = formatNarrativeBoot(layers, agentName);
3468
+ }
3469
+ return result;
3359
3470
  }
3360
3471
  export {
3361
3472
  boot,
@@ -3384,6 +3495,8 @@ export {
3384
3495
  extractIngestItems,
3385
3496
  failMaintenanceJob,
3386
3497
  findResumableMaintenanceJob,
3498
+ formatNarrativeBoot,
3499
+ formatRelativeDate,
3387
3500
  fuseHybridResults,
3388
3501
  fusionScore,
3389
3502
  getConfiguredEmbeddingProviderId,
@@ -3408,6 +3521,7 @@ export {
3408
3521
  isCountRow,
3409
3522
  listMemories,
3410
3523
  listPendingEmbeddings,
3524
+ loadWarmBootLayers,
3411
3525
  markAllEmbeddingsPending,
3412
3526
  markEmbeddingFailed,
3413
3527
  markMemoryEmbeddingPending,