@openanonymity/nanomem 0.1.0 → 0.1.2
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/LICENSE +21 -0
- package/README.md +64 -18
- package/package.json +7 -3
- package/src/backends/BaseStorage.js +147 -3
- package/src/backends/indexeddb.js +21 -8
- package/src/browser.js +227 -0
- package/src/bullets/parser.js +8 -9
- package/src/cli/auth.js +1 -1
- package/src/cli/commands.js +58 -9
- package/src/cli/config.js +1 -1
- package/src/cli/help.js +5 -2
- package/src/cli/output.js +4 -0
- package/src/cli.js +6 -3
- package/src/engine/compactor.js +3 -6
- package/src/engine/deleter.js +187 -0
- package/src/engine/executors.js +474 -11
- package/src/engine/ingester.js +98 -63
- package/src/engine/recentConversation.js +110 -0
- package/src/engine/retriever.js +243 -37
- package/src/engine/toolLoop.js +51 -9
- package/src/imports/chatgpt.js +1 -1
- package/src/imports/claude.js +85 -0
- package/src/imports/importData.js +462 -0
- package/src/imports/index.js +10 -0
- package/src/index.js +95 -2
- package/src/llm/openai.js +204 -58
- package/src/llm/tinfoil.js +508 -0
- package/src/omf.js +343 -0
- package/src/prompt_sets/conversation/ingestion.js +111 -12
- package/src/prompt_sets/document/ingestion.js +98 -4
- package/src/prompt_sets/index.js +12 -4
- package/src/types.js +135 -4
- package/src/vendor/tinfoil.browser.d.ts +2 -0
- package/src/vendor/tinfoil.browser.js +41596 -0
- package/types/backends/BaseStorage.d.ts +19 -0
- package/types/backends/indexeddb.d.ts +1 -0
- package/types/browser.d.ts +17 -0
- package/types/engine/deleter.d.ts +67 -0
- package/types/engine/executors.d.ts +56 -2
- package/types/engine/recentConversation.d.ts +18 -0
- package/types/engine/retriever.d.ts +22 -9
- package/types/imports/claude.d.ts +14 -0
- package/types/imports/importData.d.ts +29 -0
- package/types/imports/index.d.ts +2 -0
- package/types/index.d.ts +9 -0
- package/types/llm/openai.d.ts +6 -9
- package/types/llm/tinfoil.d.ts +13 -0
- package/types/omf.d.ts +40 -0
- package/types/prompt_sets/conversation/ingestion.d.ts +8 -3
- package/types/prompt_sets/document/ingestion.d.ts +8 -3
- package/types/types.d.ts +127 -2
- package/types/vendor/tinfoil.browser.d.ts +6348 -0
|
@@ -12,6 +12,16 @@ export class BaseStorage {
|
|
|
12
12
|
* @returns {Promise<string | null>}
|
|
13
13
|
*/
|
|
14
14
|
read(path: string): Promise<string | null>;
|
|
15
|
+
/**
|
|
16
|
+
* Resolve a user/model-supplied path to the canonical readable path.
|
|
17
|
+
*
|
|
18
|
+
* Returns the exact stored path when possible, or a normalized fallback
|
|
19
|
+
* match when the requested path is only approximately correct.
|
|
20
|
+
*
|
|
21
|
+
* @param {string} path
|
|
22
|
+
* @returns {Promise<string | null>}
|
|
23
|
+
*/
|
|
24
|
+
resolvePath(path: string): Promise<string | null>;
|
|
15
25
|
/**
|
|
16
26
|
* @param {string} path
|
|
17
27
|
* @param {string} content
|
|
@@ -34,6 +44,15 @@ export class BaseStorage {
|
|
|
34
44
|
/** Override for efficient path listing. Default uses exportAll(). */
|
|
35
45
|
_listAllPaths(): Promise<string[]>;
|
|
36
46
|
_parentPath(filePath: any): any;
|
|
47
|
+
_basenamePath(filePath: any): string;
|
|
48
|
+
_normalizeRequestedPath(path: any): string;
|
|
49
|
+
_normalizeLookupKey(path: any, { stripExtension }?: {
|
|
50
|
+
stripExtension?: boolean | undefined;
|
|
51
|
+
}): string;
|
|
52
|
+
_listReadablePaths(): Promise<string[]>;
|
|
53
|
+
_resolveReadablePath(path: any): Promise<any>;
|
|
54
|
+
_choosePreferredPath(candidates: any, requestedPath: any): any;
|
|
55
|
+
_pathMatchScore(candidate: any, requestedParent: any, requestedBase: any): number;
|
|
37
56
|
/** Generate a one-line summary of file content for the index. */
|
|
38
57
|
_generateOneLiner(content: any): any;
|
|
39
58
|
}
|
|
@@ -8,5 +8,6 @@ export class IndexedDBStorage extends BaseStorage {
|
|
|
8
8
|
_writeRaw(path: any, content: any, meta?: {}): Promise<void>;
|
|
9
9
|
_get(path: any): Promise<any>;
|
|
10
10
|
_getAll(): Promise<any>;
|
|
11
|
+
_sanitizeRecords(records: any): any;
|
|
11
12
|
}
|
|
12
13
|
import { BaseStorage } from './BaseStorage.js';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Remove review-only [[user_data]] markers before sending the final prompt to
|
|
3
|
+
* the frontier model.
|
|
4
|
+
*
|
|
5
|
+
* @param {string} text
|
|
6
|
+
* @returns {string}
|
|
7
|
+
*/
|
|
8
|
+
export function stripUserDataTags(text: string): string;
|
|
9
|
+
/**
|
|
10
|
+
* @param {MemoryBankConfig} [config]
|
|
11
|
+
* @returns {MemoryBank}
|
|
12
|
+
*/
|
|
13
|
+
export function createMemoryBank(config?: MemoryBankConfig): MemoryBank;
|
|
14
|
+
export * from "./bullets/index.js";
|
|
15
|
+
import type { MemoryBankConfig } from './types.js';
|
|
16
|
+
import type { MemoryBank } from './types.js';
|
|
17
|
+
export { buildOmfExport, previewOmfImport, importOmf, parseOmfText, validateOmf } from "./omf.js";
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
export class MemoryDeleter {
|
|
2
|
+
/**
|
|
3
|
+
* @param {{ backend: StorageBackend, bulletIndex: object, llmClient: LLMClient, model: string, onToolCall?: Function }} options
|
|
4
|
+
*/
|
|
5
|
+
constructor({ backend, bulletIndex, llmClient, model, onToolCall }: {
|
|
6
|
+
backend: StorageBackend;
|
|
7
|
+
bulletIndex: object;
|
|
8
|
+
llmClient: LLMClient;
|
|
9
|
+
model: string;
|
|
10
|
+
onToolCall?: Function;
|
|
11
|
+
});
|
|
12
|
+
_backend: StorageBackend;
|
|
13
|
+
_bulletIndex: any;
|
|
14
|
+
_llmClient: LLMClient;
|
|
15
|
+
_model: string;
|
|
16
|
+
_onToolCall: Function | null;
|
|
17
|
+
/**
|
|
18
|
+
* Delete memory content matching the given query.
|
|
19
|
+
*
|
|
20
|
+
* @param {string} query Plain-text description of what to delete.
|
|
21
|
+
* @param {{ deep?: boolean, mode?: string }} [options]
|
|
22
|
+
* @returns {Promise<{ status: string, deleteCalls: number, writes: Array }>}
|
|
23
|
+
*/
|
|
24
|
+
deleteForQuery(query: string, options?: {
|
|
25
|
+
deep?: boolean;
|
|
26
|
+
mode?: string;
|
|
27
|
+
}): Promise<{
|
|
28
|
+
status: string;
|
|
29
|
+
deleteCalls: number;
|
|
30
|
+
writes: any[];
|
|
31
|
+
}>;
|
|
32
|
+
_standardDelete(query: any, isDocument: any): Promise<{
|
|
33
|
+
status: string;
|
|
34
|
+
deleteCalls: number;
|
|
35
|
+
writes: never[];
|
|
36
|
+
error: string;
|
|
37
|
+
} | {
|
|
38
|
+
status: string;
|
|
39
|
+
deleteCalls: number;
|
|
40
|
+
writes: any[];
|
|
41
|
+
error?: undefined;
|
|
42
|
+
}>;
|
|
43
|
+
_deepDelete(query: any, isDocument: any): Promise<{
|
|
44
|
+
status: string;
|
|
45
|
+
deleteCalls: number;
|
|
46
|
+
writes: never[];
|
|
47
|
+
error: string;
|
|
48
|
+
} | {
|
|
49
|
+
status: string;
|
|
50
|
+
deleteCalls: number;
|
|
51
|
+
writes: any[];
|
|
52
|
+
error?: undefined;
|
|
53
|
+
}>;
|
|
54
|
+
_runDeletionLoop(query: any, systemPrompt: any, tools: any, maxIterations: any): Promise<{
|
|
55
|
+
status: string;
|
|
56
|
+
deleteCalls: number;
|
|
57
|
+
writes: never[];
|
|
58
|
+
error: string;
|
|
59
|
+
} | {
|
|
60
|
+
status: string;
|
|
61
|
+
deleteCalls: number;
|
|
62
|
+
writes: any[];
|
|
63
|
+
error?: undefined;
|
|
64
|
+
}>;
|
|
65
|
+
}
|
|
66
|
+
import type { StorageBackend } from '../types.js';
|
|
67
|
+
import type { LLMClient } from '../types.js';
|
|
@@ -13,6 +13,36 @@ export function createRetrievalExecutors(backend: StorageBackend): {
|
|
|
13
13
|
path: any;
|
|
14
14
|
}) => Promise<string>;
|
|
15
15
|
};
|
|
16
|
+
/**
|
|
17
|
+
* Build the executed augment_query tool for the retrieval flow.
|
|
18
|
+
*
|
|
19
|
+
* The outer memory-agent loop chooses relevant files. This executor then runs a
|
|
20
|
+
* dedicated prompt-crafter pass that turns those raw inputs into the final
|
|
21
|
+
* tagged prompt, keeping prompt-crafting fully inside nanomem.
|
|
22
|
+
*
|
|
23
|
+
* @param {object} options
|
|
24
|
+
* @param {StorageBackend} options.backend
|
|
25
|
+
* @param {LLMClient} options.llmClient
|
|
26
|
+
* @param {string} options.model
|
|
27
|
+
* @param {string} options.query
|
|
28
|
+
* @param {string} [options.conversationText]
|
|
29
|
+
* @param {(event: { stage: 'loading', message: string, attempt?: number }) => void} [options.onProgress]
|
|
30
|
+
*/
|
|
31
|
+
export function createAugmentQueryExecutor({ backend, llmClient, model, query, conversationText, onProgress }: {
|
|
32
|
+
backend: StorageBackend;
|
|
33
|
+
llmClient: LLMClient;
|
|
34
|
+
model: string;
|
|
35
|
+
query: string;
|
|
36
|
+
conversationText?: string | undefined;
|
|
37
|
+
onProgress?: ((event: {
|
|
38
|
+
stage: "loading";
|
|
39
|
+
message: string;
|
|
40
|
+
attempt?: number;
|
|
41
|
+
}) => void) | undefined;
|
|
42
|
+
}): ({ user_query, memory_files }: {
|
|
43
|
+
user_query: any;
|
|
44
|
+
memory_files: any;
|
|
45
|
+
}) => Promise<string>;
|
|
16
46
|
/**
|
|
17
47
|
* Build tool executors for the extraction (write) flow.
|
|
18
48
|
* @param {StorageBackend} backend
|
|
@@ -30,9 +60,9 @@ export function createExtractionExecutors(backend: StorageBackend, hooks?: Extra
|
|
|
30
60
|
path: any;
|
|
31
61
|
content: any;
|
|
32
62
|
}) => Promise<string>;
|
|
33
|
-
|
|
63
|
+
update_bullets: ({ path, updates }: {
|
|
34
64
|
path: any;
|
|
35
|
-
|
|
65
|
+
updates: any;
|
|
36
66
|
}) => Promise<string>;
|
|
37
67
|
archive_memory: ({ path, item_text }: {
|
|
38
68
|
path: any;
|
|
@@ -42,5 +72,29 @@ export function createExtractionExecutors(backend: StorageBackend, hooks?: Extra
|
|
|
42
72
|
path: any;
|
|
43
73
|
}) => Promise<string>;
|
|
44
74
|
};
|
|
75
|
+
/**
|
|
76
|
+
* Build tool executors for the deletion flow.
|
|
77
|
+
* @param {StorageBackend} backend
|
|
78
|
+
* @param {{ refreshIndex?: Function, onWrite?: Function }} [hooks]
|
|
79
|
+
*/
|
|
80
|
+
export function createDeletionExecutors(backend: StorageBackend, hooks?: {
|
|
81
|
+
refreshIndex?: Function;
|
|
82
|
+
onWrite?: Function;
|
|
83
|
+
}): {
|
|
84
|
+
list_directory: ({ dir_path }: {
|
|
85
|
+
dir_path: any;
|
|
86
|
+
}) => Promise<string>;
|
|
87
|
+
retrieve_file: ({ query }: {
|
|
88
|
+
query: any;
|
|
89
|
+
}) => Promise<string>;
|
|
90
|
+
read_file: ({ path }: {
|
|
91
|
+
path: any;
|
|
92
|
+
}) => Promise<string>;
|
|
93
|
+
delete_bullet: ({ path, bullet_text }: {
|
|
94
|
+
path: any;
|
|
95
|
+
bullet_text: any;
|
|
96
|
+
}) => Promise<string>;
|
|
97
|
+
};
|
|
45
98
|
import type { StorageBackend } from '../types.js';
|
|
99
|
+
import type { LLMClient } from '../types.js';
|
|
46
100
|
import type { ExtractionExecutorHooks } from '../types.js';
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Trim a conversation transcript while preserving turn boundaries and preferring
|
|
3
|
+
* to keep user turns visible even when assistant replies are long.
|
|
4
|
+
*
|
|
5
|
+
* @param {string} conversationText
|
|
6
|
+
* @param {object} options
|
|
7
|
+
* @param {number} [options.maxChars]
|
|
8
|
+
* @param {number} [options.maxTurns]
|
|
9
|
+
* @param {number} [options.maxUserChars]
|
|
10
|
+
* @param {number} [options.maxAssistantChars]
|
|
11
|
+
* @returns {string | null}
|
|
12
|
+
*/
|
|
13
|
+
export function trimRecentConversation(conversationText: string, { maxChars, maxTurns, maxUserChars, maxAssistantChars }?: {
|
|
14
|
+
maxChars?: number | undefined;
|
|
15
|
+
maxTurns?: number | undefined;
|
|
16
|
+
maxUserChars?: number | undefined;
|
|
17
|
+
maxAssistantChars?: number | undefined;
|
|
18
|
+
}): string | null;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export class MemoryRetriever {
|
|
2
|
+
static _stripUserDataTags(text: any): any;
|
|
2
3
|
constructor({ backend, bulletIndex, llmClient, model, onProgress, onModelText }: {
|
|
3
4
|
backend: any;
|
|
4
5
|
bulletIndex: any;
|
|
@@ -21,14 +22,24 @@ export class MemoryRetriever {
|
|
|
21
22
|
* @returns {Promise<RetrievalResult | null>}
|
|
22
23
|
*/
|
|
23
24
|
retrieveForQuery(query: string, conversationText?: string): Promise<RetrievalResult | null>;
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
25
|
+
/**
|
|
26
|
+
* @param {string} query
|
|
27
|
+
* @param {string} index
|
|
28
|
+
* @param {(event: ProgressEvent) => void | null} onProgress
|
|
29
|
+
* @param {string | undefined} conversationText
|
|
30
|
+
* @param {((text: string, iteration: number) => void) | null} onModelText
|
|
31
|
+
* @param {{ mode: 'retrieve' | 'augment' }} options
|
|
32
|
+
* @returns {Promise<RetrievalResult | AugmentQueryResult | null>}
|
|
33
|
+
*/
|
|
34
|
+
_toolCallingRetrieval(query: string, index: string, onProgress: (event: ProgressEvent) => void | null, conversationText: string | undefined, onModelText: ((text: string, iteration: number) => void) | null, options?: {
|
|
35
|
+
mode: "retrieve" | "augment";
|
|
36
|
+
}): Promise<RetrievalResult | AugmentQueryResult | null>;
|
|
37
|
+
/**
|
|
38
|
+
* @param {string} query
|
|
39
|
+
* @param {string} [conversationText]
|
|
40
|
+
* @returns {Promise<AugmentQueryResult | null>}
|
|
41
|
+
*/
|
|
42
|
+
augmentQueryForPrompt(query: string, conversationText?: string): Promise<AugmentQueryResult | null>;
|
|
32
43
|
_textSearchFallbackWithLoad(query: any, onProgress: any, conversationText: any): Promise<{
|
|
33
44
|
files: {
|
|
34
45
|
path: any;
|
|
@@ -44,7 +55,9 @@ export class MemoryRetriever {
|
|
|
44
55
|
text: string;
|
|
45
56
|
score: number;
|
|
46
57
|
}[];
|
|
47
|
-
_buildRecentContext(conversationText: any):
|
|
58
|
+
_buildRecentContext(conversationText: any): string | null;
|
|
48
59
|
_isMemoryEmpty(index: any): Promise<boolean>;
|
|
49
60
|
}
|
|
50
61
|
import type { RetrievalResult } from '../types.js';
|
|
62
|
+
import type { ProgressEvent } from '../types.js';
|
|
63
|
+
import type { AugmentQueryResult } from '../types.js';
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Detect whether parsed JSON is a Claude export.
|
|
3
|
+
* Claude exports are arrays of objects with `chat_messages` and `uuid`.
|
|
4
|
+
* @param {unknown} parsed
|
|
5
|
+
* @returns {boolean}
|
|
6
|
+
*/
|
|
7
|
+
export function isClaudeExport(parsed: unknown): boolean;
|
|
8
|
+
/**
|
|
9
|
+
* Parse a Claude export into normalized sessions.
|
|
10
|
+
* @param {unknown[]} conversations — the parsed JSON array
|
|
11
|
+
* @returns {ChatGptSession[]}
|
|
12
|
+
*/
|
|
13
|
+
export function parseClaudeExport(conversations: unknown[]): ChatGptSession[];
|
|
14
|
+
import type { ChatGptSession } from '../types.js';
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Import one or more conversations/documents into memory using the same ingest
|
|
3
|
+
* path as the CLI import command, but in a browser-safe module.
|
|
4
|
+
*
|
|
5
|
+
* @param {Pick<MemoryBank, 'init' | 'ingest'>} memoryBank
|
|
6
|
+
* @param {string | unknown | MemoryImportConversation | MemoryImportConversation[] | Array<{ path: string, content: string }>} input
|
|
7
|
+
* @param {ImportDataOptions} [options]
|
|
8
|
+
* @returns {Promise<ImportDataResult>}
|
|
9
|
+
*/
|
|
10
|
+
export function importData(memoryBank: Pick<MemoryBank, "init" | "ingest">, input: string | unknown | MemoryImportConversation | MemoryImportConversation[] | Array<{
|
|
11
|
+
path: string;
|
|
12
|
+
content: string;
|
|
13
|
+
}>, options?: ImportDataOptions): Promise<ImportDataResult>;
|
|
14
|
+
/**
|
|
15
|
+
* @param {string | unknown | MemoryImportConversation | MemoryImportConversation[] | Array<{ path: string, content: string }>} input
|
|
16
|
+
* @param {ImportDataOptions} [options]
|
|
17
|
+
* @returns {{ items: MemoryImportConversation[], mode: 'conversation' | 'document' }}
|
|
18
|
+
*/
|
|
19
|
+
export function parseImportInput(input: string | unknown | MemoryImportConversation | MemoryImportConversation[] | Array<{
|
|
20
|
+
path: string;
|
|
21
|
+
content: string;
|
|
22
|
+
}>, options?: ImportDataOptions): {
|
|
23
|
+
items: MemoryImportConversation[];
|
|
24
|
+
mode: "conversation" | "document";
|
|
25
|
+
};
|
|
26
|
+
import type { MemoryBank } from '../types.js';
|
|
27
|
+
import type { MemoryImportConversation } from '../types.js';
|
|
28
|
+
import type { ImportDataOptions } from '../types.js';
|
|
29
|
+
import type { ImportDataResult } from '../types.js';
|
package/types/imports/index.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
export { parseMarkdownFiles } from "./markdown.js";
|
|
2
2
|
export { extractSessionsFromOAFastchatExport, extractConversationFromOAFastchatExport, listOAFastchatSessions } from "./oaFastchat.js";
|
|
3
3
|
export { isChatGptExport, parseChatGptExport } from "./chatgpt.js";
|
|
4
|
+
export { isClaudeExport, parseClaudeExport } from "./claude.js";
|
|
5
|
+
export { importData, parseImportInput } from "./importData.js";
|
package/types/index.d.ts
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Remove review-only [[user_data]] markers before sending the final prompt to
|
|
3
|
+
* the frontier model.
|
|
4
|
+
*
|
|
5
|
+
* @param {string} text
|
|
6
|
+
* @returns {string}
|
|
7
|
+
*/
|
|
8
|
+
export function stripUserDataTags(text: string): string;
|
|
1
9
|
/**
|
|
2
10
|
* Create a memory instance.
|
|
3
11
|
*
|
|
@@ -18,4 +26,5 @@ import type { MemoryBankConfig } from './types.js';
|
|
|
18
26
|
import type { MemoryBank } from './types.js';
|
|
19
27
|
export { createRetrievalExecutors, createExtractionExecutors } from "./engine/executors.js";
|
|
20
28
|
export { serialize, deserialize, toZip } from "./utils/portability.js";
|
|
29
|
+
export { buildOmfExport, previewOmfImport, importOmf, parseOmfText, validateOmf } from "./omf.js";
|
|
21
30
|
export { extractSessionsFromOAFastchatExport, extractConversationFromOAFastchatExport, listOAFastchatSessions } from "./imports/oaFastchat.js";
|
package/types/llm/openai.d.ts
CHANGED
|
@@ -1,16 +1,13 @@
|
|
|
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
1
|
/**
|
|
11
2
|
* @param {LLMClientOptions} [options]
|
|
12
3
|
* @returns {LLMClient}
|
|
13
4
|
*/
|
|
14
5
|
export function createOpenAIClient({ apiKey, baseUrl, headers }?: LLMClientOptions): LLMClient;
|
|
6
|
+
export type ApiError = Error & {
|
|
7
|
+
status?: number;
|
|
8
|
+
retryable?: boolean;
|
|
9
|
+
retryAfterMs?: number | null;
|
|
10
|
+
_retryFinalized?: boolean;
|
|
11
|
+
};
|
|
15
12
|
import type { LLMClientOptions } from '../types.js';
|
|
16
13
|
import type { LLMClient } from '../types.js';
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @param {MemoryBankLLMConfig} [options]
|
|
3
|
+
* @returns {LLMClient}
|
|
4
|
+
*/
|
|
5
|
+
export function createTinfoilClient(options?: MemoryBankLLMConfig): LLMClient;
|
|
6
|
+
export type ApiError = Error & {
|
|
7
|
+
status?: number;
|
|
8
|
+
retryable?: boolean;
|
|
9
|
+
retryAfterMs?: number | null;
|
|
10
|
+
_retryFinalized?: boolean;
|
|
11
|
+
};
|
|
12
|
+
import type { MemoryBankLLMConfig } from '../types.js';
|
|
13
|
+
import type { LLMClient } from '../types.js';
|
package/types/omf.d.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @param {unknown} doc
|
|
3
|
+
* @returns {{ valid: boolean, error?: string }}
|
|
4
|
+
*/
|
|
5
|
+
export function validateOmf(doc: unknown): {
|
|
6
|
+
valid: boolean;
|
|
7
|
+
error?: string;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* @param {string} text
|
|
11
|
+
* @returns {OmfDocument}
|
|
12
|
+
*/
|
|
13
|
+
export function parseOmfText(text: string): OmfDocument;
|
|
14
|
+
/**
|
|
15
|
+
* @param {StorageFacade} storage
|
|
16
|
+
* @param {{ sourceApp?: string }} [options]
|
|
17
|
+
* @returns {Promise<OmfDocument>}
|
|
18
|
+
*/
|
|
19
|
+
export function buildOmfExport(storage: StorageFacade, options?: {
|
|
20
|
+
sourceApp?: string;
|
|
21
|
+
}): Promise<OmfDocument>;
|
|
22
|
+
/**
|
|
23
|
+
* @param {StorageFacade} storage
|
|
24
|
+
* @param {OmfDocument} doc
|
|
25
|
+
* @param {OmfImportOptions} [options]
|
|
26
|
+
* @returns {Promise<OmfImportPreview>}
|
|
27
|
+
*/
|
|
28
|
+
export function previewOmfImport(storage: StorageFacade, doc: OmfDocument, options?: OmfImportOptions): Promise<OmfImportPreview>;
|
|
29
|
+
/**
|
|
30
|
+
* @param {StorageFacade} storage
|
|
31
|
+
* @param {OmfDocument} doc
|
|
32
|
+
* @param {OmfImportOptions} [options]
|
|
33
|
+
* @returns {Promise<OmfImportResult>}
|
|
34
|
+
*/
|
|
35
|
+
export function importOmf(storage: StorageFacade, doc: OmfDocument, options?: OmfImportOptions): Promise<OmfImportResult>;
|
|
36
|
+
import type { OmfDocument } from './types.js';
|
|
37
|
+
import type { StorageFacade } from './types.js';
|
|
38
|
+
import type { OmfImportOptions } from './types.js';
|
|
39
|
+
import type { OmfImportPreview } from './types.js';
|
|
40
|
+
import type { OmfImportResult } from './types.js';
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Prompt set for conversation ingestion.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* ingestionPrompt — general import: full discretion over create/append/update.
|
|
5
|
+
* addPrompt — `nanomem add`: only write NEW facts (create or append, no updates).
|
|
6
|
+
* updatePrompt — `nanomem update`: only edit EXISTING facts (no new files).
|
|
6
7
|
*/
|
|
7
|
-
export const
|
|
8
|
+
export const addPrompt: "You are a memory manager. Save NEW facts from the text that do not yet exist in memory.\n\nCRITICAL: Only save facts the user explicitly stated. Do NOT infer, extrapolate, or fabricate.\n\nCurrent memory index:\n```\n{INDEX}\n```\n\nFor each new fact, decide:\n- Use append_memory if an existing file already covers the same domain or topic.\n- Use create_new_file only if no existing file is thematically close.\n\nDo NOT save:\n- Facts already present in memory\n- Transient details (greetings, one-off questions with no lasting answer)\n- Sensitive secrets (passwords, tokens, keys)\n\nBullet format: \"- Fact text | topic=topic-name | source=user_statement | confidence=high | updated_at=YYYY-MM-DD\"\n\nIf nothing new is worth saving, stop without calling any tools.";
|
|
9
|
+
export const updatePrompt: "You are a memory manager. Update the user's memory based on the text below.\n\nCRITICAL: Only save facts the user explicitly stated. Do NOT infer, extrapolate, or fabricate.\n\nCurrent memory index:\n```\n{INDEX}\n```\n\nSteps:\n1. Identify which existing file(s) might hold facts that are stale or contradicted by the new information.\n2. Use read_file to read the current content and find the exact bullet text to replace.\n3. If a matching old fact exists, use update_bullets with all corrections for that file in a single call, passing the exact old fact text and the corrected fact text for each.\n4. If no existing fact matches \u2014 the information is entirely new \u2014 use append_memory to add it to an existing file that covers the same domain, or create_new_file if no existing file is thematically close.\n\nRules:\n- Prefer update_bullets when an existing fact is directly contradicted or corrected.\n- Only change bullets that are directly contradicted or corrected by the new information.\n- Do not touch any other bullets in the file.\n- Pass old_fact exactly as it appears in the file (including pipe-delimited metadata is fine).\n- Pass new_fact as plain text only \u2014 no metadata.\n- When appending or creating, use this bullet format: \"- Fact text | topic=topic-name | source=user_statement | confidence=high | updated_at=YYYY-MM-DD\"\n\nIf nothing new or changed is worth saving, stop without calling any tools.";
|
|
10
|
+
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\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. Do not read files before writing. The memory index is sufficient to decide where to append. Only read a file if the index entry is ambiguous and you need the exact current content to avoid duplicating a fact.\n3. If no relevant file exists yet, create_new_file directly.\n4. 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.\n5. Use this bullet format: \"- Fact text | topic=topic-name | source=SOURCE | confidence=LEVEL | updated_at=YYYY-MM-DD\"\n6. 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.\n7. Confidence: high for direct user statements, medium for llm_infer. Never save low-confidence items.\n8. 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.\n9. 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.\n10. If a fact is time-sensitive, include date context in the text. You may optionally add review_at or expires_at.\n11. 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_bullets only if a fact is now stale or contradicted. Pass all corrections for a file in one call.\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.";
|
|
11
|
+
export const deletePrompt: "You are a memory manager performing a TARGETED deletion.\n\nThe user wants to remove: \"{QUERY}\"\n\nRULES \u2014 read carefully before acting:\n1. Delete all bullets that are ABOUT the subject(s) or entity mentioned in the deletion request.\n - If the query names a specific entity (a pet, person, place, project), delete every fact about that entity \u2014 not just the one line that introduces it.\n - Example: \"I have dog mochi\" \u2192 delete ALL facts about Mochi (habits, toys, traits, the introduction line, etc.).\n2. Do NOT delete facts about unrelated subjects, even if they appear in the same file.\n3. When genuinely unsure whether a bullet is about the target subject, SKIP it.\n4. Never delete an entire file \u2014 only individual bullets via delete_bullet.\n5. Pass the EXACT bullet text as it appears in the file, including all | metadata after the fact.\n\nCurrent memory index:\n```\n{INDEX}\n```\n\nSteps:\n1. Identify which file(s) likely contain the content to delete from the index above.\n2. Use retrieve_file or list_directory if the relevant file is not obvious from the index.\n3. Use read_file to read the identified file(s).\n4. Call delete_bullet for each bullet that is about the subject(s) in the deletion request.\n5. If nothing matches, stop without calling delete_bullet.";
|
|
12
|
+
export const deepDeletePrompt: "You are a memory manager performing a COMPREHENSIVE deletion across ALL memory files.\n\nThe user wants to remove: \"{QUERY}\"\n\nRULES \u2014 read carefully before acting:\n1. Delete all bullets that are ABOUT the subject(s) or entity mentioned in the deletion request.\n - If the query names a specific entity (a pet, person, place, project), delete every fact about that entity across every file \u2014 not just the one line that introduces it.\n - Example: \"I have dog mochi\" \u2192 delete ALL facts about Mochi wherever they appear.\n2. Do NOT delete facts about unrelated subjects.\n3. When genuinely unsure whether a bullet is about the target subject, SKIP it.\n4. Never delete an entire file \u2014 only individual bullets via delete_bullet.\n5. Pass the EXACT bullet text as it appears in the file, including all | metadata after the fact.\n\nYou MUST read every file listed below and check it for matching content.\n\nFiles to check:\n{FILE_LIST}\n\nSteps:\n1. Use read_file to read each file listed above, one by one.\n2. For each file, call delete_bullet for any bullet that is about the subject(s) in the deletion request.\n3. Continue until every file has been checked.\n4. If a file has no matching bullets, move on without calling delete_bullet for it.";
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Prompt set for document ingestion.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* ingestionPrompt — general import: full discretion over create/append/update.
|
|
5
|
+
* addPrompt — `nanomem add --format markdown`: only write NEW facts (create or append, no updates).
|
|
6
|
+
* updatePrompt — `nanomem update --format markdown`: only edit EXISTING facts (no new files).
|
|
6
7
|
*/
|
|
7
|
-
export const
|
|
8
|
+
export const addPrompt: "You are a memory manager. Extract NEW facts from the document that do not yet exist in memory.\n\nYou may extract and reasonably infer facts from what the document shows \u2014 not just word-for-word statements. Use good judgment: extract what is clearly supported by the content, avoid speculation.\n\nCurrent memory index:\n```\n{INDEX}\n```\n\nFor each new fact, decide:\n- Use append_memory if an existing file already covers the same domain or topic.\n- Use create_new_file only if no existing file is thematically close.\n\nDo NOT save:\n- Facts already present in memory\n- Boilerplate (installation steps, license text, generic disclaimers)\n- Sensitive secrets (passwords, tokens, keys)\n\nBullet format: \"- Fact text | topic=topic-name | source=document | confidence=high | updated_at=YYYY-MM-DD\"\n\nIf nothing new is worth saving, stop without calling any tools.";
|
|
9
|
+
export const updatePrompt: "You are a memory manager. Correct or update facts already saved in memory based on the document below.\n\nCRITICAL: Only edit files that already exist. Do NOT create new files. Do NOT rewrite whole files.\n\nCurrent memory index:\n```\n{INDEX}\n```\n\nSteps:\n1. Identify which existing file(s) hold facts that are now stale or contradicted by the document.\n2. Use read_file to read the current content and find the exact bullet text to replace.\n3. Use update_bullets with all corrections for that file in a single call, passing the exact old fact text and the corrected fact text for each.\n\nRules:\n- Only change bullets that are directly contradicted or corrected by the new information.\n- Do not touch any other bullets in the file.\n- Pass old_fact exactly as it appears in the file (including pipe-delimited metadata is fine).\n- Pass new_fact as plain text only \u2014 no metadata.\n\nIf nothing needs updating, stop without calling any tools.";
|
|
10
|
+
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\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. Do not read files before writing. The memory index is sufficient to decide where to append or create. Only read a file if the index entry is ambiguous and you need the exact current content to avoid duplicating a fact.\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.";
|
|
11
|
+
export const deletePrompt: "You are a memory manager performing a TARGETED deletion.\n\nThe user wants to remove: \"{QUERY}\"\n\nRULES \u2014 read carefully before acting:\n1. Delete all bullets that are ABOUT the subject(s) or entity mentioned in the deletion request.\n - If the query names a specific entity (a person, project, tool, concept), delete every fact about that entity \u2014 not just the one line that introduces it.\n - Example: \"project recipe-app\" \u2192 delete ALL facts about the recipe-app project (tech stack, goals, status, etc.).\n2. Do NOT delete facts about unrelated subjects, even if they appear in the same file.\n3. When genuinely unsure whether a bullet is about the target subject, SKIP it.\n4. Never delete an entire file \u2014 only individual bullets via delete_bullet.\n5. Pass the EXACT bullet text as it appears in the file, including all | metadata after the fact.\n\nCurrent memory index:\n```\n{INDEX}\n```\n\nSteps:\n1. Identify which file(s) likely contain the content to delete from the index above.\n2. Use retrieve_file or list_directory if the relevant file is not obvious from the index.\n3. Use read_file to read the identified file(s).\n4. Call delete_bullet for each bullet that is about the subject(s) in the deletion request.\n5. If nothing matches, stop without calling delete_bullet.";
|
|
12
|
+
export const deepDeletePrompt: "You are a memory manager performing a COMPREHENSIVE deletion across ALL memory files.\n\nThe user wants to remove: \"{QUERY}\"\n\nRULES \u2014 read carefully before acting:\n1. Delete all bullets that are ABOUT the subject(s) or entity mentioned in the deletion request.\n - If the query names a specific entity (a person, project, tool, concept), delete every fact about that entity wherever it appears.\n - Example: \"project recipe-app\" \u2192 delete ALL facts about the recipe-app project across every file.\n2. Do NOT delete facts about unrelated subjects.\n3. When genuinely unsure whether a bullet is about the target subject, SKIP it.\n4. Never delete an entire file \u2014 only individual bullets via delete_bullet.\n5. Pass the EXACT bullet text as it appears in the file, including all | metadata after the fact.\n\nYou MUST read every file listed below and check it for matching content.\n\nFiles to check:\n{FILE_LIST}\n\nSteps:\n1. Use read_file to read each file listed above, one by one.\n2. For each file, call delete_bullet for any bullet that is about the subject(s) in the deletion request.\n3. Continue until every file has been checked.\n4. If a file has no matching bullets, move on without calling delete_bullet for it.";
|