hoomanjs 1.28.2 → 1.28.3

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 (65) hide show
  1. package/README.md +8 -9
  2. package/dist/acp/sessions/config-options.js +1 -1
  3. package/dist/acp/sessions/config-options.js.map +1 -1
  4. package/dist/acp/utils/tool-kind.js +4 -4
  5. package/dist/chat/index.js +1 -1
  6. package/dist/chat/index.js.map +1 -1
  7. package/dist/configure/app.js +2 -56
  8. package/dist/configure/app.js.map +1 -1
  9. package/dist/core/agent/index.d.ts +1 -1
  10. package/dist/core/agent/index.js +14 -2
  11. package/dist/core/agent/index.js.map +1 -1
  12. package/dist/core/config.d.ts +10 -18
  13. package/dist/core/config.js +11 -31
  14. package/dist/core/config.js.map +1 -1
  15. package/dist/core/memory/index.d.ts +1 -0
  16. package/dist/core/memory/index.js +1 -0
  17. package/dist/core/memory/index.js.map +1 -1
  18. package/dist/core/memory/ltm/llama-embed.d.ts +28 -0
  19. package/dist/core/memory/ltm/llama-embed.js +204 -0
  20. package/dist/core/memory/ltm/llama-embed.js.map +1 -0
  21. package/dist/core/memory/ltm/sqlite.d.ts +12 -0
  22. package/dist/core/memory/ltm/sqlite.js +101 -0
  23. package/dist/core/memory/ltm/sqlite.js.map +1 -0
  24. package/dist/core/memory/ltm/store.d.ts +13 -6
  25. package/dist/core/memory/ltm/store.js +192 -92
  26. package/dist/core/memory/ltm/store.js.map +1 -1
  27. package/dist/core/memory/ltm/tools.js +4 -4
  28. package/dist/core/memory/ltm/utils.d.ts +9 -11
  29. package/dist/core/memory/ltm/utils.js +14 -15
  30. package/dist/core/memory/ltm/utils.js.map +1 -1
  31. package/dist/core/memory/wiki/index.d.ts +4 -0
  32. package/dist/core/memory/wiki/index.js +4 -0
  33. package/dist/core/memory/wiki/index.js.map +1 -0
  34. package/dist/core/memory/wiki/store.d.ts +37 -0
  35. package/dist/core/memory/wiki/store.js +317 -0
  36. package/dist/core/memory/wiki/store.js.map +1 -0
  37. package/dist/core/memory/wiki/tools.d.ts +11 -0
  38. package/dist/core/memory/wiki/tools.js +291 -0
  39. package/dist/core/memory/wiki/tools.js.map +1 -0
  40. package/dist/core/memory/wiki/types.d.ts +25 -0
  41. package/dist/core/memory/wiki/types.js +2 -0
  42. package/dist/core/memory/wiki/types.js.map +1 -0
  43. package/dist/core/prompts/static/ltm.md +4 -4
  44. package/dist/core/prompts/system.d.ts +4 -0
  45. package/dist/core/prompts/system.js +36 -4
  46. package/dist/core/prompts/system.js.map +1 -1
  47. package/dist/core/skills/built-in/hooman-config/SKILL.md +5 -17
  48. package/dist/core/state/tool-approvals.js +5 -5
  49. package/dist/core/tools/index.d.ts +1 -1
  50. package/dist/core/tools/index.js +1 -1
  51. package/dist/core/tools/index.js.map +1 -1
  52. package/dist/core/tools/plan.d.ts +2 -2
  53. package/dist/core/utils/paths.d.ts +6 -0
  54. package/dist/core/utils/paths.js +6 -0
  55. package/dist/core/utils/paths.js.map +1 -1
  56. package/dist/index.d.ts +4 -5
  57. package/dist/index.js +3 -4
  58. package/dist/index.js.map +1 -1
  59. package/package.json +12 -3
  60. package/dist/core/memory/ltm/embed.d.ts +0 -16
  61. package/dist/core/memory/ltm/embed.js +0 -49
  62. package/dist/core/memory/ltm/embed.js.map +0 -1
  63. package/dist/core/tools/wiki.d.ts +0 -11
  64. package/dist/core/tools/wiki.js +0 -560
  65. package/dist/core/tools/wiki.js.map +0 -1
@@ -1,3 +1,4 @@
1
1
  export { create as createShortTermMemory } from "./stm/index.js";
2
2
  export { createLongTermMemoryStore, createLongTermMemoryTools, LongTermMemoryStore, } from "./ltm/index.js";
3
+ export { WikiStore, createWikiStore } from "./wiki/index.js";
3
4
  export type { ArchiveMemoryInput, LongTermMemoryOptions, LongTermMemoryScope, MemorySource, SearchMemoryInput, SearchMemoryResult, StoreMemoryInput, StoreMemoryResult, UpdateMemoryInput, } from "./ltm/index.js";
@@ -1,3 +1,4 @@
1
1
  export { create as createShortTermMemory } from "./stm/index.js";
2
2
  export { createLongTermMemoryStore, createLongTermMemoryTools, LongTermMemoryStore, } from "./ltm/index.js";
3
+ export { WikiStore, createWikiStore } from "./wiki/index.js";
3
4
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/core/memory/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AACjE,OAAO,EACL,yBAAyB,EACzB,yBAAyB,EACzB,mBAAmB,GACpB,MAAM,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/core/memory/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AACjE,OAAO,EACL,yBAAyB,EACzB,yBAAyB,EACzB,mBAAmB,GACpB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC"}
@@ -0,0 +1,28 @@
1
+ export declare function isQwen3EmbeddingModel(modelUri: string): boolean;
2
+ export declare function formatQueryForEmbedding(query: string, modelUri?: string): string;
3
+ export declare function formatDocForEmbedding(text: string, title: string | undefined, modelUri?: string): string;
4
+ export type LtmLlamaEmbedderOptions = {
5
+ modelUri: string;
6
+ cacheDir: string;
7
+ };
8
+ /**
9
+ * Lazy local GGUF embeddings (node-llama-cpp), matching QMD formatting rules.
10
+ */
11
+ export declare class LtmLlamaEmbedder {
12
+ private readonly modelUri;
13
+ private readonly cacheDir;
14
+ private llama;
15
+ private model;
16
+ private context;
17
+ private loadPromise;
18
+ constructor(options: LtmLlamaEmbedderOptions);
19
+ get resolvedModelUri(): string;
20
+ /** Load GGUF + embedding context (no forward pass). Call at agent startup to avoid blocking tools. */
21
+ warmup(): Promise<void>;
22
+ private ensureLoaded;
23
+ private resolveEmbedTokenLimit;
24
+ private truncateToContextSize;
25
+ embedQuery(query: string): Promise<Float32Array>;
26
+ embedDocument(content: string): Promise<Float32Array>;
27
+ dispose(): Promise<void>;
28
+ }
@@ -0,0 +1,204 @@
1
+ import { getLlama, resolveModelFile, LlamaLogLevel, } from "node-llama-cpp";
2
+ import { closeSync, existsSync, mkdirSync, openSync, readSync, statSync, unlinkSync, } from "node:fs";
3
+ const GGUF_MAGIC = Buffer.from("GGUF");
4
+ function validateGgufFile(filePath, modelUri) {
5
+ if (!existsSync(filePath)) {
6
+ return;
7
+ }
8
+ const fd = openSync(filePath, "r");
9
+ const sniff = Buffer.alloc(512);
10
+ try {
11
+ readSync(fd, sniff, 0, 512, 0);
12
+ }
13
+ finally {
14
+ closeSync(fd);
15
+ }
16
+ const header = sniff.subarray(0, 4);
17
+ if (header.equals(GGUF_MAGIC)) {
18
+ return;
19
+ }
20
+ const text = sniff.toString("utf-8").toLowerCase();
21
+ const isHtml = text.includes("<!doctype") || text.includes("<html");
22
+ const got = header.toString("utf-8");
23
+ const sizeKB = (statSync(filePath).size / 1024).toFixed(0);
24
+ unlinkSync(filePath);
25
+ if (isHtml) {
26
+ throw new Error(`Downloaded LTM embed model is an HTML page, not GGUF (${sizeKB} KB).\n` +
27
+ `Model: ${modelUri}\nPath: ${filePath}`);
28
+ }
29
+ throw new Error(`LTM embed model is not valid GGUF (expected "GGUF", got "${got}", ${sizeKB} KB).\n` +
30
+ `Model: ${modelUri}`);
31
+ }
32
+ export function isQwen3EmbeddingModel(modelUri) {
33
+ return /qwen.*embed/i.test(modelUri) || /embed.*qwen/i.test(modelUri);
34
+ }
35
+ export function formatQueryForEmbedding(query, modelUri) {
36
+ const uri = modelUri ?? "";
37
+ if (isQwen3EmbeddingModel(uri)) {
38
+ return `Instruct: Retrieve relevant documents for the given query\nQuery: ${query}`;
39
+ }
40
+ return `task: search result | query: ${query}`;
41
+ }
42
+ export function formatDocForEmbedding(text, title, modelUri) {
43
+ const uri = modelUri ?? "";
44
+ if (isQwen3EmbeddingModel(uri)) {
45
+ return title ? `${title}\n${text}` : text;
46
+ }
47
+ return `title: ${title || "none"} | text: ${text}`;
48
+ }
49
+ function resolveLlamaGpuMode() {
50
+ if (process.env.CI) {
51
+ return false;
52
+ }
53
+ const raw = process.env.HOOMAN_LTM_LLAMA_GPU?.trim() ||
54
+ process.env.QMD_LLAMA_GPU?.trim() ||
55
+ "";
56
+ const normalized = raw.toLowerCase();
57
+ if (!normalized) {
58
+ return "auto";
59
+ }
60
+ if (["false", "off", "none", "disable", "disabled", "0"].includes(normalized)) {
61
+ return false;
62
+ }
63
+ if (normalized === "metal" ||
64
+ normalized === "vulkan" ||
65
+ normalized === "cuda") {
66
+ return normalized;
67
+ }
68
+ return "auto";
69
+ }
70
+ function resolveEmbedContextSize() {
71
+ const v = Number.parseInt(process.env.HOOMAN_LTM_EMBED_CONTEXT_SIZE ?? "", 10);
72
+ if (Number.isFinite(v) && v > 0) {
73
+ return v;
74
+ }
75
+ return 2048;
76
+ }
77
+ const EMBED_CONTEXT_SIZE = resolveEmbedContextSize();
78
+ /**
79
+ * Lazy local GGUF embeddings (node-llama-cpp), matching QMD formatting rules.
80
+ */
81
+ export class LtmLlamaEmbedder {
82
+ modelUri;
83
+ cacheDir;
84
+ llama = null;
85
+ model = null;
86
+ context = null;
87
+ loadPromise = null;
88
+ constructor(options) {
89
+ this.modelUri = options.modelUri;
90
+ this.cacheDir = options.cacheDir;
91
+ }
92
+ get resolvedModelUri() {
93
+ return this.modelUri;
94
+ }
95
+ /** Load GGUF + embedding context (no forward pass). Call at agent startup to avoid blocking tools. */
96
+ async warmup() {
97
+ await this.ensureLoaded();
98
+ }
99
+ async ensureLoaded() {
100
+ if (this.context) {
101
+ return;
102
+ }
103
+ if (this.loadPromise) {
104
+ await this.loadPromise;
105
+ return;
106
+ }
107
+ this.loadPromise = (async () => {
108
+ if (!existsSync(this.cacheDir)) {
109
+ mkdirSync(this.cacheDir, { recursive: true });
110
+ }
111
+ const modelPath = await resolveModelFile(this.modelUri, this.cacheDir);
112
+ validateGgufFile(modelPath, this.modelUri);
113
+ const gpuMode = resolveLlamaGpuMode();
114
+ const loadLlama = async (gpu) => getLlama({
115
+ build: "never",
116
+ logLevel: LlamaLogLevel.error,
117
+ gpu,
118
+ });
119
+ let llama;
120
+ if (gpuMode === false) {
121
+ llama = await loadLlama(false);
122
+ }
123
+ else {
124
+ try {
125
+ llama = await loadLlama(gpuMode);
126
+ }
127
+ catch {
128
+ llama = await loadLlama(false);
129
+ }
130
+ }
131
+ this.llama = llama;
132
+ const model = await llama.loadModel({ modelPath });
133
+ this.model = model;
134
+ const trained = model.trainContextSize;
135
+ const contextSize = typeof trained === "number" && Number.isFinite(trained) && trained > 0
136
+ ? Math.max(1, Math.min(EMBED_CONTEXT_SIZE, trained))
137
+ : EMBED_CONTEXT_SIZE;
138
+ this.context = await model.createEmbeddingContext({ contextSize });
139
+ })();
140
+ try {
141
+ await this.loadPromise;
142
+ }
143
+ finally {
144
+ this.loadPromise = null;
145
+ }
146
+ }
147
+ resolveEmbedTokenLimit() {
148
+ const trained = this.model?.trainContextSize;
149
+ if (typeof trained === "number" &&
150
+ Number.isFinite(trained) &&
151
+ trained > 0) {
152
+ return Math.max(1, Math.min(EMBED_CONTEXT_SIZE, trained));
153
+ }
154
+ return EMBED_CONTEXT_SIZE;
155
+ }
156
+ truncateToContextSize(text) {
157
+ if (!this.model) {
158
+ return text;
159
+ }
160
+ const maxTokens = this.resolveEmbedTokenLimit();
161
+ if (maxTokens <= 0) {
162
+ return text;
163
+ }
164
+ const tokens = this.model.tokenize(text);
165
+ if (tokens.length <= maxTokens) {
166
+ return text;
167
+ }
168
+ const safeLimit = Math.max(1, maxTokens - 4);
169
+ const truncatedTokens = tokens.slice(0, safeLimit);
170
+ return this.model.detokenize(truncatedTokens);
171
+ }
172
+ async embedQuery(query) {
173
+ await this.ensureLoaded();
174
+ if (!this.context || !this.model) {
175
+ throw new Error("LTM embed context not initialized");
176
+ }
177
+ const formatted = formatQueryForEmbedding(query, this.modelUri);
178
+ const safe = this.truncateToContextSize(formatted);
179
+ const result = await this.context.getEmbeddingFor(safe);
180
+ return new Float32Array(result.vector);
181
+ }
182
+ async embedDocument(content) {
183
+ await this.ensureLoaded();
184
+ if (!this.context || !this.model) {
185
+ throw new Error("LTM embed context not initialized");
186
+ }
187
+ const formatted = formatDocForEmbedding(content, undefined, this.modelUri);
188
+ const safe = this.truncateToContextSize(formatted);
189
+ const result = await this.context.getEmbeddingFor(safe);
190
+ return new Float32Array(result.vector);
191
+ }
192
+ async dispose() {
193
+ if (this.context) {
194
+ await this.context.dispose();
195
+ this.context = null;
196
+ }
197
+ if (this.model) {
198
+ await this.model.dispose();
199
+ this.model = null;
200
+ }
201
+ this.llama = null;
202
+ }
203
+ }
204
+ //# sourceMappingURL=llama-embed.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"llama-embed.js","sourceRoot":"","sources":["../../../../src/core/memory/ltm/llama-embed.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import Database from "better-sqlite3";
2
+ export type LtmDatabase = Database.Database;
3
+ declare const META_DIMS = "embedding_dims";
4
+ declare const META_MODEL = "embedding_model_id";
5
+ export declare function loadSqliteVecExtension(db: LtmDatabase): void;
6
+ export declare function verifySqliteVecLoaded(db: LtmDatabase): void;
7
+ export declare function openLtmDatabase(path: string): LtmDatabase;
8
+ export declare function getStoreMeta(db: LtmDatabase, key: string): string | null;
9
+ export declare function setStoreMeta(db: LtmDatabase, key: string, value: string): void;
10
+ export declare function ensureVecTable(db: LtmDatabase, dimensions: number): void;
11
+ export declare function persistEmbeddingSchemaMeta(db: LtmDatabase, modelId: string, dimensions: number): void;
12
+ export { META_DIMS, META_MODEL };
@@ -0,0 +1,101 @@
1
+ import Database from "better-sqlite3";
2
+ import * as sqliteVec from "sqlite-vec";
3
+ const META_DIMS = "embedding_dims";
4
+ const META_MODEL = "embedding_model_id";
5
+ export function loadSqliteVecExtension(db) {
6
+ sqliteVec.load(db);
7
+ }
8
+ export function verifySqliteVecLoaded(db) {
9
+ try {
10
+ const row = db.prepare(`SELECT vec_version() AS version`).get();
11
+ if (!row?.version || typeof row.version !== "string") {
12
+ throw new Error("vec_version() returned no version");
13
+ }
14
+ }
15
+ catch (err) {
16
+ const msg = err instanceof Error ? err.message : String(err);
17
+ throw new Error(`sqlite-vec extension is unavailable (probe failed: ${msg}). ` +
18
+ "Ensure sqlite-vec native bindings are installed for your platform.");
19
+ }
20
+ }
21
+ function migrateSchema(db) {
22
+ db.exec(`
23
+ CREATE TABLE IF NOT EXISTS ltm_memories (
24
+ id TEXT PRIMARY KEY NOT NULL,
25
+ user_id TEXT NOT NULL,
26
+ type TEXT NOT NULL,
27
+ status TEXT NOT NULL,
28
+ content TEXT NOT NULL,
29
+ importance REAL NOT NULL,
30
+ strength REAL NOT NULL,
31
+ access_count INTEGER NOT NULL,
32
+ confidence REAL,
33
+ created_at INTEGER NOT NULL,
34
+ updated_at INTEGER,
35
+ last_accessed_at INTEGER,
36
+ version INTEGER NOT NULL,
37
+ source TEXT NOT NULL,
38
+ tags_json TEXT,
39
+ entities_json TEXT,
40
+ related_to_json TEXT,
41
+ superseded_by TEXT
42
+ );
43
+ CREATE INDEX IF NOT EXISTS idx_ltm_user_status ON ltm_memories(user_id, status);
44
+ CREATE INDEX IF NOT EXISTS idx_ltm_user_type ON ltm_memories(user_id, type);
45
+
46
+ CREATE TABLE IF NOT EXISTS ltm_store_meta (
47
+ key TEXT PRIMARY KEY,
48
+ value TEXT NOT NULL
49
+ );
50
+ `);
51
+ }
52
+ export function openLtmDatabase(path) {
53
+ const db = new Database(path);
54
+ db.pragma("journal_mode = WAL");
55
+ db.pragma("foreign_keys = ON");
56
+ loadSqliteVecExtension(db);
57
+ verifySqliteVecLoaded(db);
58
+ migrateSchema(db);
59
+ return db;
60
+ }
61
+ export function getStoreMeta(db, key) {
62
+ const row = db
63
+ .prepare(`SELECT value FROM ltm_store_meta WHERE key = ?`)
64
+ .get(key);
65
+ return row?.value ?? null;
66
+ }
67
+ export function setStoreMeta(db, key, value) {
68
+ db.prepare(`INSERT INTO ltm_store_meta (key, value) VALUES (?, ?)
69
+ ON CONFLICT(key) DO UPDATE SET value = excluded.value`).run(key, value);
70
+ }
71
+ export function ensureVecTable(db, dimensions) {
72
+ const tableInfo = db
73
+ .prepare(`SELECT sql FROM sqlite_master WHERE type='table' AND name='vectors_vec'`)
74
+ .get();
75
+ if (tableInfo) {
76
+ const match = tableInfo.sql.match(/float\[(\d+)\]/);
77
+ const hasMemoryId = tableInfo.sql.includes("memory_id");
78
+ const hasCosine = tableInfo.sql.includes("distance_metric=cosine");
79
+ const existingDims = match?.[1] ? Number.parseInt(match[1], 10) : null;
80
+ if (existingDims === dimensions && hasMemoryId && hasCosine) {
81
+ return;
82
+ }
83
+ if (existingDims !== null && existingDims !== dimensions) {
84
+ throw new Error(`LTM embedding dimension mismatch: existing vectors are ${existingDims}d but the current model produces ${dimensions}d. ` +
85
+ "Change back to the previous embed model or delete ~/.hooman/ltm.sqlite to rebuild (data loss).");
86
+ }
87
+ db.exec("DROP TABLE IF EXISTS vectors_vec");
88
+ }
89
+ db.exec(`CREATE VIRTUAL TABLE vectors_vec USING vec0(memory_id TEXT PRIMARY KEY, embedding float[${dimensions}] distance_metric=cosine)`);
90
+ }
91
+ export function persistEmbeddingSchemaMeta(db, modelId, dimensions) {
92
+ const existingModel = getStoreMeta(db, META_MODEL);
93
+ if (existingModel != null && existingModel !== modelId) {
94
+ throw new Error(`LTM database was built with embed model "${existingModel}" but config requests "${modelId}". ` +
95
+ "Change DEFAULT_LTM_EMBED_MODEL in Hooman or delete ltm.sqlite to rebuild (data loss).");
96
+ }
97
+ setStoreMeta(db, META_MODEL, modelId);
98
+ setStoreMeta(db, META_DIMS, String(dimensions));
99
+ }
100
+ export { META_DIMS, META_MODEL };
101
+ //# sourceMappingURL=sqlite.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sqlite.js","sourceRoot":"","sources":["../../../../src/core/memory/ltm/sqlite.ts"],"names":[],"mappings":"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"}
@@ -1,12 +1,19 @@
1
1
  import type { ArchiveMemoryInput, LongTermMemoryOptions, LongTermMemoryScope, Memory, SearchMemoryInput, SearchMemoryResult, StoreMemoryInput, StoreMemoryResult, UpdateMemoryInput } from "./types.js";
2
- import type { Config } from "../../config.js";
2
+ import { type Config } from "../../config.js";
3
3
  export declare class LongTermMemoryStore {
4
- private readonly config;
5
4
  private readonly options;
6
- private readonly client;
7
- private collectionPromise;
8
- constructor(config: Config, options?: LongTermMemoryOptions);
9
- private collection;
5
+ private readonly db;
6
+ private readonly embedder;
7
+ constructor(_config: Config, options?: LongTermMemoryOptions);
8
+ /**
9
+ * Preload the local embed model so the first memory_search / memory_store call
10
+ * does not block the LLM turn (otherwise the UI can sit at "thinking" with 0 tokens).
11
+ */
12
+ warmup(): Promise<void>;
13
+ private hasVectorTable;
14
+ private ensureEmbeddingMeta;
15
+ private insertMemoryRow;
16
+ private updateMemoryRow;
10
17
  count(scope?: LongTermMemoryScope): Promise<number>;
11
18
  get(id: string): Promise<Memory | null>;
12
19
  search(input: SearchMemoryInput): Promise<SearchMemoryResult[]>;