@openanonymity/nanomem 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/README.md +194 -0
  2. package/package.json +85 -0
  3. package/src/backends/BaseStorage.js +177 -0
  4. package/src/backends/filesystem.js +177 -0
  5. package/src/backends/indexeddb.js +208 -0
  6. package/src/backends/ram.js +113 -0
  7. package/src/backends/schema.js +42 -0
  8. package/src/bullets/bulletIndex.js +125 -0
  9. package/src/bullets/compaction.js +109 -0
  10. package/src/bullets/index.js +16 -0
  11. package/src/bullets/normalize.js +241 -0
  12. package/src/bullets/parser.js +199 -0
  13. package/src/bullets/scoring.js +53 -0
  14. package/src/cli/auth.js +323 -0
  15. package/src/cli/commands.js +411 -0
  16. package/src/cli/config.js +120 -0
  17. package/src/cli/diff.js +68 -0
  18. package/src/cli/help.js +84 -0
  19. package/src/cli/output.js +269 -0
  20. package/src/cli/spinner.js +54 -0
  21. package/src/cli.js +178 -0
  22. package/src/engine/compactor.js +247 -0
  23. package/src/engine/executors.js +152 -0
  24. package/src/engine/ingester.js +229 -0
  25. package/src/engine/retriever.js +414 -0
  26. package/src/engine/toolLoop.js +176 -0
  27. package/src/imports/chatgpt.js +160 -0
  28. package/src/imports/index.js +14 -0
  29. package/src/imports/markdown.js +104 -0
  30. package/src/imports/oaFastchat.js +124 -0
  31. package/src/index.js +199 -0
  32. package/src/llm/anthropic.js +264 -0
  33. package/src/llm/openai.js +179 -0
  34. package/src/prompt_sets/conversation/ingestion.js +51 -0
  35. package/src/prompt_sets/document/ingestion.js +43 -0
  36. package/src/prompt_sets/index.js +31 -0
  37. package/src/types.js +382 -0
  38. package/src/utils/portability.js +174 -0
  39. package/types/backends/BaseStorage.d.ts +42 -0
  40. package/types/backends/filesystem.d.ts +11 -0
  41. package/types/backends/indexeddb.d.ts +12 -0
  42. package/types/backends/ram.d.ts +8 -0
  43. package/types/backends/schema.d.ts +14 -0
  44. package/types/bullets/bulletIndex.d.ts +47 -0
  45. package/types/bullets/compaction.d.ts +10 -0
  46. package/types/bullets/index.d.ts +36 -0
  47. package/types/bullets/normalize.d.ts +95 -0
  48. package/types/bullets/parser.d.ts +31 -0
  49. package/types/bullets/scoring.d.ts +12 -0
  50. package/types/engine/compactor.d.ts +27 -0
  51. package/types/engine/executors.d.ts +46 -0
  52. package/types/engine/ingester.d.ts +29 -0
  53. package/types/engine/retriever.d.ts +50 -0
  54. package/types/engine/toolLoop.d.ts +9 -0
  55. package/types/imports/chatgpt.d.ts +14 -0
  56. package/types/imports/index.d.ts +3 -0
  57. package/types/imports/markdown.d.ts +31 -0
  58. package/types/imports/oaFastchat.d.ts +30 -0
  59. package/types/index.d.ts +21 -0
  60. package/types/llm/anthropic.d.ts +16 -0
  61. package/types/llm/openai.d.ts +16 -0
  62. package/types/prompt_sets/conversation/ingestion.d.ts +7 -0
  63. package/types/prompt_sets/document/ingestion.d.ts +7 -0
  64. package/types/prompt_sets/index.d.ts +11 -0
  65. package/types/types.d.ts +293 -0
  66. package/types/utils/portability.d.ts +33 -0
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Compact a list of bullets: deduplicate, assign tiers, enforce limits.
3
+ * @param {Bullet[]} bullets
4
+ * @param {CompactBulletsOptions} [options]
5
+ * @returns {CompactionResult}
6
+ */
7
+ export function compactBullets(bullets: Bullet[], options?: CompactBulletsOptions): CompactionResult;
8
+ import type { Bullet } from '../types.js';
9
+ import type { CompactBulletsOptions } from '../types.js';
10
+ import type { CompactionResult } from '../types.js';
@@ -0,0 +1,36 @@
1
+ export * from "./normalize.js";
2
+ export * from "./parser.js";
3
+ export * from "./scoring.js";
4
+ export * from "./compaction.js";
5
+ /**
6
+ * Barrel re-export for all bullet utilities.
7
+ */
8
+ export type Tier = import("../types.js").Tier;
9
+ /**
10
+ * Barrel re-export for all bullet utilities.
11
+ */
12
+ export type Status = import("../types.js").Status;
13
+ /**
14
+ * Barrel re-export for all bullet utilities.
15
+ */
16
+ export type Source = import("../types.js").Source;
17
+ /**
18
+ * Barrel re-export for all bullet utilities.
19
+ */
20
+ export type Confidence = import("../types.js").Confidence;
21
+ /**
22
+ * Barrel re-export for all bullet utilities.
23
+ */
24
+ export type Bullet = import("../types.js").Bullet;
25
+ /**
26
+ * Barrel re-export for all bullet utilities.
27
+ */
28
+ export type EnsureBulletMetadataOptions = import("../types.js").EnsureBulletMetadataOptions;
29
+ /**
30
+ * Barrel re-export for all bullet utilities.
31
+ */
32
+ export type CompactionResult = import("../types.js").CompactionResult;
33
+ /**
34
+ * Barrel re-export for all bullet utilities.
35
+ */
36
+ export type CompactBulletsOptions = import("../types.js").CompactBulletsOptions;
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Normalization utilities for memory bullet metadata.
3
+ * @import { Tier, Status, Source, Confidence, Bullet, EnsureBulletMetadataOptions } from '../types.js'
4
+ */
5
+ /**
6
+ * @param {string | number | null | undefined} value
7
+ * @returns {string | null}
8
+ */
9
+ export function safeDateIso(value: string | number | null | undefined): string | null;
10
+ /** @returns {string} */
11
+ export function todayIsoDate(): string;
12
+ /**
13
+ * @param {string} path
14
+ * @returns {string}
15
+ */
16
+ export function inferTopicFromPath(path: string): string;
17
+ /**
18
+ * @param {string} value
19
+ * @param {string} [fallback]
20
+ * @returns {string}
21
+ */
22
+ export function normalizeTopic(value: string, fallback?: string): string;
23
+ /**
24
+ * @param {string} value
25
+ * @returns {string}
26
+ */
27
+ export function normalizeFactText(value: string): string;
28
+ /**
29
+ * @param {string | null | undefined} value
30
+ * @param {Tier} [fallback]
31
+ * @returns {Tier}
32
+ */
33
+ export function normalizeTier(value: string | null | undefined, fallback?: Tier): Tier;
34
+ /**
35
+ * @param {string | null | undefined} value
36
+ * @param {Status} [fallback]
37
+ * @returns {Status}
38
+ */
39
+ export function normalizeStatus(value: string | null | undefined, fallback?: Status): Status;
40
+ /**
41
+ * @param {string | null | undefined} value
42
+ * @param {Source} [fallback]
43
+ * @returns {Source}
44
+ */
45
+ export function normalizeSource(value: string | null | undefined, fallback?: Source): Source;
46
+ /**
47
+ * @param {string | null | undefined} value
48
+ * @param {Confidence} [fallback]
49
+ * @returns {Confidence}
50
+ */
51
+ export function normalizeConfidence(value: string | null | undefined, fallback?: Confidence): Confidence;
52
+ /**
53
+ * @param {Source | string | null | undefined} source
54
+ * @returns {Confidence}
55
+ */
56
+ export function defaultConfidenceForSource(source: Source | string | null | undefined): Confidence;
57
+ /**
58
+ * @param {string} section
59
+ * @returns {Tier}
60
+ */
61
+ export function inferTierFromSection(section: string): Tier;
62
+ /**
63
+ * @param {string} section
64
+ * @returns {Status}
65
+ */
66
+ export function inferStatusFromSection(section: string): Status;
67
+ /**
68
+ * @param {string} value
69
+ * @returns {Tier}
70
+ */
71
+ export function normalizeTierToSection(value: string): Tier;
72
+ /**
73
+ * @param {Partial<Bullet>} bullet
74
+ * @param {Tier} [fallback]
75
+ * @returns {Tier}
76
+ */
77
+ export function inferTierFromBullet(bullet: Partial<Bullet>, fallback?: Tier): Tier;
78
+ /**
79
+ * @param {Partial<Bullet>} bullet
80
+ * @param {EnsureBulletMetadataOptions} [options]
81
+ * @returns {Bullet}
82
+ */
83
+ export function ensureBulletMetadata(bullet: Partial<Bullet>, options?: EnsureBulletMetadataOptions): Bullet;
84
+ /**
85
+ * @param {Bullet} bullet
86
+ * @param {string} [today]
87
+ * @returns {boolean}
88
+ */
89
+ export function isExpiredBullet(bullet: Bullet, today?: string): boolean;
90
+ import type { Tier } from '../types.js';
91
+ import type { Status } from '../types.js';
92
+ import type { Source } from '../types.js';
93
+ import type { Confidence } from '../types.js';
94
+ import type { Bullet } from '../types.js';
95
+ import type { EnsureBulletMetadataOptions } from '../types.js';
@@ -0,0 +1,31 @@
1
+ /**
2
+ * @param {string} content
3
+ * @returns {Bullet[]}
4
+ */
5
+ export function parseBullets(content: string): Bullet[];
6
+ /**
7
+ * @param {string} content
8
+ * @returns {number}
9
+ */
10
+ export function countBullets(content: string): number;
11
+ /**
12
+ * @param {string} content
13
+ * @returns {string[]}
14
+ */
15
+ export function extractTitles(content: string): string[];
16
+ /**
17
+ * @param {Partial<Bullet>} bullet
18
+ * @returns {string}
19
+ */
20
+ export function renderBullet(bullet: Partial<Bullet>): string;
21
+ /**
22
+ * @param {Bullet[]} working
23
+ * @param {Bullet[]} longTerm
24
+ * @param {Bullet[]} history
25
+ * @param {{ titleTopic?: string }} [options]
26
+ * @returns {string}
27
+ */
28
+ export function renderCompactedDocument(working: Bullet[], longTerm: Bullet[], history: Bullet[], options?: {
29
+ titleTopic?: string;
30
+ }): string;
31
+ import type { Bullet } from '../types.js';
@@ -0,0 +1,12 @@
1
+ /**
2
+ * @param {Partial<Bullet>} bullet
3
+ * @param {string[]} [queryTerms]
4
+ * @returns {number}
5
+ */
6
+ export function scoreBullet(bullet: Partial<Bullet>, queryTerms?: string[]): number;
7
+ /**
8
+ * @param {string} query
9
+ * @returns {string[]}
10
+ */
11
+ export function tokenizeQuery(query: string): string[];
12
+ import type { Bullet } from '../types.js';
@@ -0,0 +1,27 @@
1
+ export class MemoryCompactor {
2
+ constructor({ backend, bulletIndex, llmClient, model, onProgress }: {
3
+ backend: any;
4
+ bulletIndex: any;
5
+ llmClient: any;
6
+ model: any;
7
+ onProgress: any;
8
+ });
9
+ _backend: any;
10
+ _bulletIndex: any;
11
+ _llmClient: any;
12
+ _model: any;
13
+ _onProgress: any;
14
+ _running: boolean;
15
+ _fileSummaries: any[];
16
+ compactAll(): Promise<{
17
+ filesChanged: number;
18
+ filesTotal: any;
19
+ } | undefined>;
20
+ _compactFile(path: any, content: any): Promise<any>;
21
+ /**
22
+ * Ask the LLM which Working bullets are now stale/completed/superseded.
23
+ * Returns the same array with superseded bullets marked status=superseded.
24
+ */
25
+ _semanticReviewWorking(working: any, longTerm: any, path: any): Promise<any>;
26
+ _llmRewrite(path: any, raw: any): Promise<any>;
27
+ }
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Build tool executors for the retrieval (read) flow.
3
+ * @param {StorageBackend} backend
4
+ */
5
+ export function createRetrievalExecutors(backend: StorageBackend): {
6
+ list_directory: ({ dir_path }: {
7
+ dir_path: any;
8
+ }) => Promise<string>;
9
+ retrieve_file: ({ query }: {
10
+ query: any;
11
+ }) => Promise<string>;
12
+ read_file: ({ path }: {
13
+ path: any;
14
+ }) => Promise<string>;
15
+ };
16
+ /**
17
+ * Build tool executors for the extraction (write) flow.
18
+ * @param {StorageBackend} backend
19
+ * @param {ExtractionExecutorHooks} [hooks]
20
+ */
21
+ export function createExtractionExecutors(backend: StorageBackend, hooks?: ExtractionExecutorHooks): {
22
+ read_file: ({ path }: {
23
+ path: any;
24
+ }) => Promise<string>;
25
+ create_new_file: ({ path, content }: {
26
+ path: any;
27
+ content: any;
28
+ }) => Promise<string>;
29
+ append_memory: ({ path, content }: {
30
+ path: any;
31
+ content: any;
32
+ }) => Promise<string>;
33
+ update_memory: ({ path, content }: {
34
+ path: any;
35
+ content: any;
36
+ }) => Promise<string>;
37
+ archive_memory: ({ path, item_text }: {
38
+ path: any;
39
+ item_text: any;
40
+ }) => Promise<string>;
41
+ delete_memory: ({ path }: {
42
+ path: any;
43
+ }) => Promise<string>;
44
+ };
45
+ import type { StorageBackend } from '../types.js';
46
+ import type { ExtractionExecutorHooks } from '../types.js';
@@ -0,0 +1,29 @@
1
+ export class MemoryIngester {
2
+ constructor({ backend, bulletIndex, llmClient, model, onToolCall }: {
3
+ backend: any;
4
+ bulletIndex: any;
5
+ llmClient: any;
6
+ model: any;
7
+ onToolCall: any;
8
+ });
9
+ _backend: any;
10
+ _bulletIndex: any;
11
+ _llmClient: any;
12
+ _model: any;
13
+ _onToolCall: any;
14
+ /**
15
+ * Ingest memory from a conversation.
16
+ *
17
+ * @param {Message[]} messages
18
+ * @param {IngestOptions} [options]
19
+ * @returns {Promise<IngestResult>}
20
+ */
21
+ ingest(messages: Message[], options?: IngestOptions): Promise<IngestResult>;
22
+ _buildConversationText(messages: any): string;
23
+ _buildDocumentText(messages: any): any;
24
+ _normalizeGeneratedContent(content: any, path: any, updatedAt: any, isDocument?: boolean): any;
25
+ _mergeWithExisting(existing: any, incoming: any, path: any, updatedAt: any, isDocument?: boolean): string;
26
+ }
27
+ import type { Message } from '../types.js';
28
+ import type { IngestOptions } from '../types.js';
29
+ import type { IngestResult } from '../types.js';
@@ -0,0 +1,50 @@
1
+ export class MemoryRetriever {
2
+ constructor({ backend, bulletIndex, llmClient, model, onProgress, onModelText }: {
3
+ backend: any;
4
+ bulletIndex: any;
5
+ llmClient: any;
6
+ model: any;
7
+ onProgress: any;
8
+ onModelText: any;
9
+ });
10
+ _backend: any;
11
+ _bulletIndex: any;
12
+ _llmClient: any;
13
+ _model: any;
14
+ _onProgress: any;
15
+ _onModelText: any;
16
+ /**
17
+ * Retrieve relevant memory context for a user query.
18
+ *
19
+ * @param {string} query the user's message text
20
+ * @param {string} [conversationText] current session text for reference resolution
21
+ * @returns {Promise<RetrievalResult | null>}
22
+ */
23
+ retrieveForQuery(query: string, conversationText?: string): Promise<RetrievalResult | null>;
24
+ _toolCallingRetrieval(query: any, index: any, onProgress: any, conversationText: any, onModelText: any): Promise<{
25
+ files: {
26
+ path: any;
27
+ content: string;
28
+ }[];
29
+ paths: any[];
30
+ assembledContext: any;
31
+ } | null>;
32
+ _textSearchFallbackWithLoad(query: any, onProgress: any, conversationText: any): Promise<{
33
+ files: {
34
+ path: any;
35
+ content: any;
36
+ }[];
37
+ paths: any[];
38
+ assembledContext: string | null;
39
+ } | null>;
40
+ _textSearchFallback(query: any): Promise<any[]>;
41
+ _buildSnippetContext(paths: any, query: any, conversationText: any): Promise<string | null>;
42
+ _filterRedundantContext(assembledContext: any, conversationText: any): any;
43
+ _scoreRawLines(content: any, queryTerms: any): {
44
+ text: string;
45
+ score: number;
46
+ }[];
47
+ _buildRecentContext(conversationText: any): any;
48
+ _isMemoryEmpty(index: any): Promise<boolean>;
49
+ }
50
+ import type { RetrievalResult } from '../types.js';
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Run an agentic tool-calling loop.
3
+ *
4
+ * @param {ToolLoopOptions} options
5
+ * @returns {Promise<ToolLoopResult>}
6
+ */
7
+ export function runAgenticToolLoop(options: ToolLoopOptions): Promise<ToolLoopResult>;
8
+ import type { ToolLoopOptions } from '../types.js';
9
+ import type { ToolLoopResult } from '../types.js';
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Detect whether parsed JSON is a ChatGPT export.
3
+ * ChatGPT exports are arrays of objects with `mapping` and `current_node`.
4
+ * @param {unknown} parsed
5
+ * @returns {boolean}
6
+ */
7
+ export function isChatGptExport(parsed: unknown): boolean;
8
+ /**
9
+ * Parse a ChatGPT export into normalized sessions.
10
+ * @param {unknown[]} conversations — the parsed JSON array
11
+ * @returns {ChatGptSession[]}
12
+ */
13
+ export function parseChatGptExport(conversations: unknown[]): ChatGptSession[];
14
+ import type { ChatGptSession } from '../types.js';
@@ -0,0 +1,3 @@
1
+ export { parseMarkdownFiles } from "./markdown.js";
2
+ export { extractSessionsFromOAFastchatExport, extractConversationFromOAFastchatExport, listOAFastchatSessions } from "./oaFastchat.js";
3
+ export { isChatGptExport, parseChatGptExport } from "./chatgpt.js";
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Plain markdown file importer.
3
+ *
4
+ * Converts markdown files into sessions that can be fed to ingest().
5
+ * Each file becomes a single-message conversation with the file content
6
+ * as a user message, so the LLM extracts structured facts from it.
7
+ *
8
+ * Usage (library):
9
+ * import { parseMarkdownFiles } from '@openanonymity/nanomem/imports'
10
+ * const sessions = parseMarkdownFiles([{ path: 'notes/health.md', content: '...' }])
11
+ * for (const session of sessions) await mem.ingest(session.messages)
12
+ *
13
+ * Usage (CLI):
14
+ * memory import notes.md --format markdown
15
+ * memory import notes-dir/ --format markdown
16
+ */
17
+ /** @import { Message, ChatGptSession } from '../types.js' */
18
+ /**
19
+ * Parse one or more markdown documents into sessions for ingestion.
20
+ *
21
+ * Accepts either a single markdown string or an array of { path, content } records
22
+ * (e.g. from reading a directory of .md files).
23
+ *
24
+ * @param {string | { path: string; content: string }[]} input
25
+ * @returns {ChatGptSession[]}
26
+ */
27
+ export function parseMarkdownFiles(input: string | {
28
+ path: string;
29
+ content: string;
30
+ }[]): ChatGptSession[];
31
+ import type { ChatGptSession } from '../types.js';
@@ -0,0 +1,30 @@
1
+ /**
2
+ * @param {any} exportJson
3
+ * @returns {SessionSummary[]}
4
+ */
5
+ export function listOAFastchatSessions(exportJson: any): SessionSummary[];
6
+ /**
7
+ * @param {any} exportJson
8
+ * @param {{ sessionId?: string, sessionTitle?: string }} [options]
9
+ * @returns {SessionWithConversation[]}
10
+ */
11
+ export function extractSessionsFromOAFastchatExport(exportJson: any, options?: {
12
+ sessionId?: string;
13
+ sessionTitle?: string;
14
+ }): SessionWithConversation[];
15
+ /**
16
+ * @param {any} exportJson
17
+ * @param {{ sessionId?: string, sessionTitle?: string }} [options]
18
+ * @returns {{ session: SessionSummary | null, sessions: SessionSummary[], conversation: Message[] }}
19
+ */
20
+ export function extractConversationFromOAFastchatExport(exportJson: any, options?: {
21
+ sessionId?: string;
22
+ sessionTitle?: string;
23
+ }): {
24
+ session: SessionSummary | null;
25
+ sessions: SessionSummary[];
26
+ conversation: Message[];
27
+ };
28
+ import type { SessionSummary } from '../types.js';
29
+ import type { SessionWithConversation } from '../types.js';
30
+ import type { Message } from '../types.js';
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Create a memory instance.
3
+ *
4
+ * @param {MemoryBankConfig} [config]
5
+ * @returns {MemoryBank}
6
+ */
7
+ export function createMemoryBank(config?: MemoryBankConfig): MemoryBank;
8
+ export * from "./types.js";
9
+ export { createOpenAIClient } from "./llm/openai.js";
10
+ export { createAnthropicClient } from "./llm/anthropic.js";
11
+ export { InMemoryStorage } from "./backends/ram.js";
12
+ export { BaseStorage } from "./backends/BaseStorage.js";
13
+ export { MemoryBulletIndex } from "./bullets/bulletIndex.js";
14
+ export { MemoryRetriever } from "./engine/retriever.js";
15
+ export { MemoryIngester } from "./engine/ingester.js";
16
+ export { MemoryCompactor } from "./engine/compactor.js";
17
+ import type { MemoryBankConfig } from './types.js';
18
+ import type { MemoryBank } from './types.js';
19
+ export { createRetrievalExecutors, createExtractionExecutors } from "./engine/executors.js";
20
+ export { serialize, deserialize, toZip } from "./utils/portability.js";
21
+ export { extractSessionsFromOAFastchatExport, extractConversationFromOAFastchatExport, listOAFastchatSessions } from "./imports/oaFastchat.js";
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Anthropic Messages API client.
3
+ *
4
+ * Translates the standard LLM client interface (OpenAI Chat Completions format)
5
+ * into Anthropic's Messages API format, so the memory system can use Claude models.
6
+ *
7
+ * Uses `fetch` (built into Node 18+ and browsers).
8
+ */
9
+ /** @import { ChatCompletionParams, ChatCompletionResponse, LLMClient, LLMClientOptions, LLMMessage, ToolCall, ToolDefinition } from '../types.js' */
10
+ /**
11
+ * @param {LLMClientOptions} [options]
12
+ * @returns {LLMClient}
13
+ */
14
+ export function createAnthropicClient({ apiKey, baseUrl, headers }?: LLMClientOptions): LLMClient;
15
+ import type { LLMClientOptions } from '../types.js';
16
+ import type { LLMClient } from '../types.js';
@@ -0,0 +1,16 @@
1
+ /**
2
+ * OpenAI-compatible HTTP client.
3
+ *
4
+ * Works with OpenAI, Tinfoil, OpenRouter, or any provider
5
+ * that implements the OpenAI Chat Completions API format.
6
+ *
7
+ * Uses `fetch` (built into Node 18+ and browsers).
8
+ */
9
+ /** @import { ChatCompletionParams, ChatCompletionResponse, LLMClient, LLMClientOptions, ToolCall } from '../types.js' */
10
+ /**
11
+ * @param {LLMClientOptions} [options]
12
+ * @returns {LLMClient}
13
+ */
14
+ export function createOpenAIClient({ apiKey, baseUrl, headers }?: LLMClientOptions): LLMClient;
15
+ import type { LLMClientOptions } from '../types.js';
16
+ import type { LLMClient } from '../types.js';
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Prompt set for conversation ingestion.
3
+ *
4
+ * Strict mode: only saves facts the user explicitly stated.
5
+ * Used when importing chat history, conversation logs, or live sessions.
6
+ */
7
+ export const ingestionPrompt: "You are a memory manager. After reading a conversation, decide if any concrete, reusable facts should be saved to the user's memory files.\n\nCRITICAL: Only save facts the user explicitly stated. Do NOT infer, extrapolate, or fabricate information.\n\nSave information that is likely to help in a future conversation. Be selective \u2014 only save durable facts, not transient conversation details.\n\nDo NOT save:\n- Anything the user did not explicitly say (no inferences, no extrapolations, no \"likely\" facts)\n- Information already present in existing files (the system deduplicates automatically)\n- Transient details (greetings, \"help me with this\", \"thanks\", questions without lasting answers)\n- The assistant's own reasoning, suggestions, or knowledge \u2014 only what the user stated\n- Sensitive secrets (passwords, auth tokens, private keys, full payment data, government IDs)\n- Opinions the assistant expressed unless the user explicitly agreed with them\n\nCurrent memory index:\n```\n{INDEX}\n```\n\n**Key principle: Prefer fewer, broader files over many narrow ones.** Organize files into folders by domain (e.g. health/, work/, personal/). Within each folder, group related facts into the same file rather than splitting every sub-topic into its own file. Before creating a new file, check whether an existing file in the same domain could absorb the facts. A single file with many bullets on related sub-topics is better than many files with one or two bullets each.\n\nInstructions:\n1. Read the conversation below and identify facts the user explicitly stated.\n2. Check the memory index above. Default to append_memory when an existing file covers the same domain or a closely related topic. Only use create_new_file when no existing file is thematically close. Do not read files before writing \u2014 the system deduplicates automatically.\n3. Use this bullet format: \"- Fact text | topic=topic-name | source=SOURCE | confidence=LEVEL | updated_at=YYYY-MM-DD\"\n4. Source values:\n - source=user_statement \u2014 the user directly said this. This is the PRIMARY source. Use it for the vast majority of saved facts.\n - source=llm_infer \u2014 use ONLY when combining multiple explicit user statements into an obvious conclusion (e.g. user said \"I work at Acme\" and \"Acme is in SF\" \u2192 \"Works in SF\"). Never use this to guess, extrapolate, or fill in gaps. When in doubt, do not save.\n5. Confidence: high for direct user statements, medium for llm_infer. Never save low-confidence items.\n6. You may optionally add tier=working for clearly short-term or in-progress context. If you are unsure, omit tier and just save the fact.\n7. Facts worth saving: allergies, health conditions, location, job/role, tech stack, pets, family members, durable preferences, and active plans \u2014 but ONLY if the user explicitly mentioned them.\n8. If a fact is time-sensitive, include date context in the text. You may optionally add review_at or expires_at.\n9. If nothing new is worth remembering, simply stop without calling any write tools. Saving nothing is better than saving something wrong.\n\nRules:\n- Write facts in a timeless, archival format: use absolute dates (YYYY-MM-DD) rather than relative terms like \"recently\", \"currently\", \"just\", or \"last week\". A fact must be interpretable correctly even years after it was written.\n- Favor broad thematic files. A file can hold multiple related sub-topics \u2014 only truly unrelated facts need separate files.\n- Only create a new file when nothing in the index is thematically close. When in doubt, append.\n- When creating a new file, choose a broad, thematic name that can absorb future related facts \u2014 not a narrow label for a single detail.\n- Use update_memory only if a fact is now stale or contradicted.\n- When a new explicit user statement contradicts an older one on the same topic, prefer the newer statement. If a user statement conflicts with an inference, the user statement always wins.\n- If a conflict is ambiguous, preserve both versions rather than deleting one.\n- Do not skip obvious facts just because the schema supports extra metadata.\n- Content should be raw facts only \u2014 no filler commentary.";
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Prompt set for document ingestion.
3
+ *
4
+ * Relaxed mode: extracts and reasonably infers facts from reference material.
5
+ * Used when importing notes, READMEs, articles, code repositories, or knowledge bases.
6
+ */
7
+ export const ingestionPrompt: "You are a memory manager. You are reading documents (notes, README files, code repositories, articles) and extracting facts about the subject into a structured memory bank.\n\nUnlike conversation ingestion, you may extract and reasonably infer facts from what the documents show \u2014 not just what was explicitly stated word-for-word. Use good judgment: extract what is clearly supported by the content, avoid speculation.\n\nSave information that would be useful when answering questions about this subject in the future. Be generous \u2014 capture expertise, projects, preferences, philosophy, and patterns that emerge from the documents.\n\nDo NOT save:\n- Speculation or guesses not supported by the content\n- Boilerplate (installation steps, license text, generic disclaimers)\n- Information already present in existing files (use read_file to check first)\n- Sensitive secrets (passwords, auth tokens, private keys)\n\nCurrent memory index:\n```\n{INDEX}\n```\n\n**Key principle: Create a NEW file for each distinct topic.** Organize into domain folders (e.g. projects/, expertise/, education/, philosophy/) with topic-specific files within them.\n\nInstructions:\n1. Read the document content and identify concrete, reusable facts about the subject.\n2. If a matching file already exists in the index, use read_file first to avoid duplicates.\n3. Use create_new_file for new topics, append_memory to add to existing files.\n4. Use this bullet format: \"- Fact text | topic=topic-name | source=SOURCE | confidence=LEVEL | updated_at=YYYY-MM-DD\"\n5. Source values (IMPORTANT \u2014 never use source=user_statement here):\n - source=document \u2014 the fact is directly stated or clearly shown in the document. Use for the majority of facts.\n - source=document_infer \u2014 a reasonable inference from what multiple parts of the document collectively show (e.g. a repo with only C files and a README praising simplicity \u2192 \"prefers low-level, minimal implementations\"). Use sparingly.\n6. Confidence: high for source=document facts, medium for source=document_infer.\n7. Facts worth extracting: skills and expertise, projects built, stated opinions and philosophy, tools and languages used, patterns across work, goals and motivations, background and experience.\n8. If nothing meaningful can be extracted from a document, stop without calling any write tools.\n\nRules:\n- Write facts in a timeless, archival format: use absolute dates (YYYY-MM-DD) rather than relative terms like \"recently\", \"currently\", \"just\", or \"last week\". A fact must be interpretable correctly even years after it was written.\n- One file per distinct topic. Do NOT put unrelated facts in the same file.\n- Create new files freely \u2014 focused files are better than bloated ones.\n- Content should be raw facts only \u2014 no filler commentary.";
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Resolve the prompt set for a given mode.
3
+ * Falls back to 'conversation' for unknown modes.
4
+ *
5
+ * @param {string} [mode]
6
+ * @returns {{ ingestionPrompt: string }}
7
+ */
8
+ export function resolvePromptSet(mode?: string): {
9
+ ingestionPrompt: string;
10
+ };
11
+ export const AVAILABLE_MODES: string[];