opencode-harnmem 0.0.0-stage → 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 (87) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +45 -2
  3. package/index.js +512 -0
  4. package/lib/engine.mjs +72 -0
  5. package/lib/format.mjs +119 -0
  6. package/lib/settings.mjs +131 -0
  7. package/package.json +23 -3
  8. package/vendor/core/assets.d.ts +52 -0
  9. package/vendor/core/assets.d.ts.map +1 -0
  10. package/vendor/core/assets.js +227 -0
  11. package/vendor/core/assets.js.map +1 -0
  12. package/vendor/core/config.d.ts +36 -0
  13. package/vendor/core/config.d.ts.map +1 -0
  14. package/vendor/core/config.js +134 -0
  15. package/vendor/core/config.js.map +1 -0
  16. package/vendor/core/consolidate.d.ts +14 -0
  17. package/vendor/core/consolidate.d.ts.map +1 -0
  18. package/vendor/core/consolidate.js +237 -0
  19. package/vendor/core/consolidate.js.map +1 -0
  20. package/vendor/core/data/synonyms.en.txt +61 -0
  21. package/vendor/core/data/synonyms.zh.txt +157 -0
  22. package/vendor/core/extract.d.ts +49 -0
  23. package/vendor/core/extract.d.ts.map +1 -0
  24. package/vendor/core/extract.js +179 -0
  25. package/vendor/core/extract.js.map +1 -0
  26. package/vendor/core/frontmatter.d.ts +31 -0
  27. package/vendor/core/frontmatter.d.ts.map +1 -0
  28. package/vendor/core/frontmatter.js +372 -0
  29. package/vendor/core/frontmatter.js.map +1 -0
  30. package/vendor/core/harnmem.d.ts +87 -0
  31. package/vendor/core/harnmem.d.ts.map +1 -0
  32. package/vendor/core/harnmem.js +918 -0
  33. package/vendor/core/harnmem.js.map +1 -0
  34. package/vendor/core/hot-context.d.ts +28 -0
  35. package/vendor/core/hot-context.d.ts.map +1 -0
  36. package/vendor/core/hot-context.js +179 -0
  37. package/vendor/core/hot-context.js.map +1 -0
  38. package/vendor/core/index.d.ts +16 -0
  39. package/vendor/core/index.d.ts.map +1 -0
  40. package/vendor/core/index.js +10 -0
  41. package/vendor/core/index.js.map +1 -0
  42. package/vendor/core/indexer.d.ts +107 -0
  43. package/vendor/core/indexer.d.ts.map +1 -0
  44. package/vendor/core/indexer.js +282 -0
  45. package/vendor/core/indexer.js.map +1 -0
  46. package/vendor/core/inspect.d.ts +278 -0
  47. package/vendor/core/inspect.d.ts.map +1 -0
  48. package/vendor/core/inspect.js +643 -0
  49. package/vendor/core/inspect.js.map +1 -0
  50. package/vendor/core/lifecycle.d.ts +50 -0
  51. package/vendor/core/lifecycle.d.ts.map +1 -0
  52. package/vendor/core/lifecycle.js +249 -0
  53. package/vendor/core/lifecycle.js.map +1 -0
  54. package/vendor/core/project.d.ts +41 -0
  55. package/vendor/core/project.d.ts.map +1 -0
  56. package/vendor/core/project.js +197 -0
  57. package/vendor/core/project.js.map +1 -0
  58. package/vendor/core/prompts/README.md +96 -0
  59. package/vendor/core/prompts/consolidate-digest.md +31 -0
  60. package/vendor/core/prompts/extract.md +80 -0
  61. package/vendor/core/prompts/migrate.md +96 -0
  62. package/vendor/core/prompts/synonyms.md +43 -0
  63. package/vendor/core/prompts/write-policy.md +12 -0
  64. package/vendor/core/retrieval.d.ts +95 -0
  65. package/vendor/core/retrieval.d.ts.map +1 -0
  66. package/vendor/core/retrieval.js +569 -0
  67. package/vendor/core/retrieval.js.map +1 -0
  68. package/vendor/core/store.d.ts +145 -0
  69. package/vendor/core/store.d.ts.map +1 -0
  70. package/vendor/core/store.js +511 -0
  71. package/vendor/core/store.js.map +1 -0
  72. package/vendor/core/suggest.d.ts +128 -0
  73. package/vendor/core/suggest.d.ts.map +1 -0
  74. package/vendor/core/suggest.js +384 -0
  75. package/vendor/core/suggest.js.map +1 -0
  76. package/vendor/core/text.d.ts +80 -0
  77. package/vendor/core/text.d.ts.map +1 -0
  78. package/vendor/core/text.js +253 -0
  79. package/vendor/core/text.js.map +1 -0
  80. package/vendor/core/types.d.ts +453 -0
  81. package/vendor/core/types.d.ts.map +1 -0
  82. package/vendor/core/types.js +14 -0
  83. package/vendor/core/types.js.map +1 -0
  84. package/vendor/core/util.d.ts +38 -0
  85. package/vendor/core/util.d.ts.map +1 -0
  86. package/vendor/core/util.js +248 -0
  87. package/vendor/core/util.js.map +1 -0
package/lib/format.mjs ADDED
@@ -0,0 +1,119 @@
1
+ /**
2
+ * Model-facing text formatting for MCP tool results and /memory output.
3
+ * Port of packages/pi-harnmem/src/format.ts so every harness shows identical
4
+ * tool-output shapes. Tool results are the only place where live data may
5
+ * appear; the injected snapshot stays byte-stable (nothing here is injected).
6
+ */
7
+
8
+ const MAX_TOOL_TEXT = 12_000;
9
+
10
+ function clip(text, max = MAX_TOOL_TEXT) {
11
+ if (text.length <= max) return text;
12
+ return text.slice(0, max) + `\n…[truncated ${text.length - max} chars]`;
13
+ }
14
+
15
+ function fmtTime(ms) {
16
+ if (!ms) return "-";
17
+ return new Date(ms).toISOString().slice(0, 10);
18
+ }
19
+
20
+ export function formatWrite(result) {
21
+ const lines = [];
22
+ switch (result.action) {
23
+ case "NEW":
24
+ lines.push(`stored ${result.id} (NEW)`);
25
+ break;
26
+ case "SKIP":
27
+ lines.push(`already exists ${result.id} (SKIP)`);
28
+ break;
29
+ case "MERGE":
30
+ lines.push(`merged into ${result.id} (MERGE)`);
31
+ break;
32
+ case "SUPERSEDE":
33
+ lines.push(`superseded ${result.superseded.join(", ") || "(none)"} → ${result.id}`);
34
+ break;
35
+ case "LINK":
36
+ lines.push(`linked to existing ${result.id} (LINK)`);
37
+ break;
38
+ default:
39
+ lines.push(`write result: ${result.action} ${result.id}`);
40
+ }
41
+ if (result.warnings?.length) lines.push(`warnings: ${result.warnings.join("; ")}`);
42
+ // Contract (docs/adapter-authoring.md §2): the write tool returns exactly one
43
+ // line — "stored mem_x (NEW)" / "(SKIP)" / "superseded a → b" — no guidance.
44
+ return clip(lines.join("\n"));
45
+ }
46
+
47
+ export function formatSearch(result) {
48
+ if (result.abstained || result.results.length === 0) {
49
+ return clip(
50
+ `no confident memories for "${result.query}" (abstained). Try different keywords, or the memory status tool to inspect the store.`,
51
+ );
52
+ }
53
+ const lines = [`${result.results.length}/${result.total} matches for "${result.query}":`];
54
+ for (const hit of result.results) {
55
+ const flags = [hit.status !== "active" ? hit.status : "", hit.pinned ? "pinned" : "", hit.historical ? "historical" : ""]
56
+ .filter(Boolean)
57
+ .join(",");
58
+ lines.push(
59
+ `- ${hit.id} [${hit.type}${hit.category ? "/" + hit.category : ""}${flags ? " (" + flags + ")" : ""}] ${hit.title} — ${hit.description}` +
60
+ (hit.snippet ? `\n ${hit.snippet.replace(/\s+/g, " ").slice(0, 240)}` : ""),
61
+ );
62
+ }
63
+ if (result.debug) {
64
+ // Defensive (matches the codex adapter): shards is typed string[] but a
65
+ // hostile/host-reshaped object must not throw inside a tool result.
66
+ const shards = Array.isArray(result.debug.shards) ? result.debug.shards.join(",") : String(result.debug.shards ?? "?");
67
+ lines.push(`debug: candidates=${result.debug.candidateCount} shards=${shards} elapsed=${result.debug.elapsedMs}ms`);
68
+ }
69
+ lines.push("Call the memory read tool with a hit id to open the record.");
70
+ return clip(lines.join("\n"));
71
+ }
72
+
73
+ export function formatRead(result, id) {
74
+ if (!result) return `not found: ${id}`;
75
+ const r = result.record;
76
+ const lines = [
77
+ `${r.id} [${r.type}${r.category ? "/" + r.category : ""}] ${r.scope}${r.project ? "/" + r.project : ""} status=${r.status} importance=${r.importance} confidence=${r.confidence}${r.pinned ? " pinned" : ""}`,
78
+ `title: ${r.title}`,
79
+ `description: ${r.description}`,
80
+ `updated: ${fmtTime(r.updated)} created: ${fmtTime(r.created)} valid: ${fmtTime(r.validFrom)} → ${r.validTo ? fmtTime(r.validTo) : "now"}`,
81
+ ];
82
+ if (r.tags?.length) lines.push(`tags: ${r.tags.join(", ")}`);
83
+ if (r.entities?.length) lines.push(`entities: ${r.entities.join(", ")}`);
84
+ if (r.links?.length) lines.push(`links: ${r.links.join(", ")}`);
85
+ if (result.backlinks?.length) lines.push(`backlinks: ${result.backlinks.join(", ")}`);
86
+ if (result.history?.length) lines.push(`history: ${result.history.map((h) => h.id).join(", ")}`);
87
+ lines.push("", r.content);
88
+ return clip(lines.join("\n"));
89
+ }
90
+
91
+ export function formatForget(result) {
92
+ return result.ok
93
+ ? result.mode === "hard"
94
+ ? `permanently deleted ${result.id}`
95
+ : `moved ${result.id} to trash (restorable for the grace period; restore with the harnmem CLI)`
96
+ : `could not forget ${result.id}`;
97
+ }
98
+
99
+ export function formatStats(stats) {
100
+ const lines = [
101
+ `harnmem v${stats.version} home=${stats.home}`,
102
+ `project: ${stats.project ?? "(none)"}`,
103
+ `index: seq=${stats.index.seq} stale=${stats.index.stale} built=${fmtTime(stats.index.builtAt)} size=${stats.index.sizeBytes}B overhead=${(stats.index.overhead * 100).toFixed(0)}%`,
104
+ ];
105
+ for (const shard of stats.shards) {
106
+ lines.push(
107
+ `shard ${shard.name} [${shard.scope}] total=${shard.total} active=${shard.active} superseded=${shard.superseded} archived=${shard.archived} pinned=${shard.pinned} ${Math.round(shard.bytes / 1024)}KB`,
108
+ );
109
+ }
110
+ if (stats.shards.length === 0) lines.push("no shards yet (empty store)");
111
+ lines.push(
112
+ `health: overBudget=${stats.health.overBudget} duplicates=${stats.health.duplicateCandidates} orphans=${stats.health.orphans} lastConsolidate=${stats.health.lastConsolidate ? fmtTime(stats.health.lastConsolidate) : "never"}`,
113
+ );
114
+ lines.push(
115
+ `extraction: pending=${stats.extraction.pending} failed=${stats.extraction.failed}` +
116
+ (Object.keys(stats.extraction.skipReasons).length ? ` skip=${JSON.stringify(stats.extraction.skipReasons)}` : ""),
117
+ );
118
+ return clip(lines.join("\n"));
119
+ }
@@ -0,0 +1,131 @@
1
+ /**
2
+ * Settings resolution for the opencode adapter.
3
+ *
4
+ * Priority (low -> high), matching docs/design/07-adapter-contract.md §7:
5
+ * 1. `<home>/config.json` (default `~/.harnmem/config.json`)
6
+ * 2. `<cwd>/.harnmem.json` (project config)
7
+ * 3. environment variables (HARNMEM_HOME, HARNMEM_DISABLED, ...)
8
+ *
9
+ * The chain and defaults are identical to the claude adapter; only the
10
+ * max-inject-tokens env var is harness-scoped. Malformed JSON is ignored,
11
+ * never fatal.
12
+ */
13
+
14
+ import { existsSync, readFileSync } from "node:fs";
15
+ import { homedir } from "node:os";
16
+ import path from "node:path";
17
+
18
+ const KNOWN_KEYS = ["enabled", "readOnly", "inject", "autoExtract", "debug", "home", "project", "maxInjectTokens"];
19
+
20
+ const DEFAULTS = {
21
+ enabled: true,
22
+ readOnly: false,
23
+ inject: true,
24
+ autoExtract: false,
25
+ debug: false,
26
+ maxInjectTokens: 1200,
27
+ };
28
+
29
+ function truthy(value) {
30
+ if (value === undefined) return undefined;
31
+ const v = String(value).trim().toLowerCase();
32
+ if (v === "" || v === "0" || v === "false" || v === "no" || v === "off") return false;
33
+ return true;
34
+ }
35
+
36
+ /**
37
+ * Expand a leading `~` to the user home before path.resolve(): core would
38
+ * expand it, but by then the adapter has already resolved `~/x` into
39
+ * `<cwd>\~\x`. Probed failure mode shared with the other adapters.
40
+ */
41
+ function expandHome(value) {
42
+ if (typeof value !== "string") return value;
43
+ const t = value.trim();
44
+ if (t === "~") return homedir();
45
+ if (t.startsWith("~/") || t.startsWith("~\\")) return path.join(homedir(), t.slice(2));
46
+ return t;
47
+ }
48
+
49
+ function readSettingsFile(file) {
50
+ if (!existsSync(file)) return undefined;
51
+ try {
52
+ const parsed = JSON.parse(readFileSync(file, "utf8"));
53
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return undefined;
54
+ return parsed;
55
+ } catch {
56
+ return undefined;
57
+ }
58
+ }
59
+
60
+ function pickKnown(source) {
61
+ if (!source) return {};
62
+ const out = {};
63
+ // Skip null explicitly: "enabled": null in a config file must fall back to
64
+ // the default (true), not be treated as a falsy override that disables the
65
+ // store.
66
+ for (const key of KNOWN_KEYS) {
67
+ if (source[key] === undefined || source[key] === null) continue;
68
+ out[key] = source[key];
69
+ }
70
+ return out;
71
+ }
72
+
73
+ const BOOL_KEYS = ["enabled", "readOnly", "inject", "autoExtract", "debug"];
74
+
75
+ /**
76
+ * Config files are user-edited JSON: coerce boolean-ish strings the same way
77
+ * the env path does ("enabled": "false" must disable, not stay truthy), and
78
+ * validate maxInjectTokens like the env path (no strings/zero/negatives
79
+ * straight into core's token budget).
80
+ */
81
+ function coerceFileValues(settings) {
82
+ for (const key of BOOL_KEYS) {
83
+ if (typeof settings[key] !== "string") continue;
84
+ const v = truthy(settings[key]);
85
+ if (v === undefined) delete settings[key];
86
+ else settings[key] = v;
87
+ }
88
+ if (settings.maxInjectTokens !== undefined) {
89
+ const n = Number(settings.maxInjectTokens);
90
+ if (!Number.isFinite(n) || n <= 0) delete settings.maxInjectTokens;
91
+ else settings.maxInjectTokens = Math.floor(n);
92
+ }
93
+ return settings;
94
+ }
95
+
96
+ export function resolveSettings(cwd = process.cwd(), env = process.env) {
97
+ const envHome = expandHome(env.HARNMEM_HOME);
98
+ const baseHome = envHome && envHome.trim() ? path.resolve(envHome) : path.join(homedir(), ".harnmem");
99
+
100
+ let settings = { ...DEFAULTS };
101
+ settings = coerceFileValues({ ...settings, ...pickKnown(readSettingsFile(path.join(baseHome, "config.json"))) });
102
+ settings = coerceFileValues({ ...settings, ...pickKnown(readSettingsFile(path.join(cwd, ".harnmem.json"))) });
103
+
104
+ const disabled = truthy(env.HARNMEM_DISABLED);
105
+ if (disabled !== undefined) settings.enabled = !disabled;
106
+ const ro = truthy(env.HARNMEM_READONLY);
107
+ if (ro !== undefined) settings.readOnly = ro;
108
+ const dbg = truthy(env.HARNMEM_DEBUG);
109
+ if (dbg !== undefined) settings.debug = dbg;
110
+ const ae = truthy(env.HARNMEM_AUTO_EXTRACT);
111
+ if (ae !== undefined) settings.autoExtract = ae;
112
+ const inj = truthy(env.HARNMEM_INJECT);
113
+ if (inj !== undefined) settings.inject = inj;
114
+ if (env.HARNMEM_OPENCODE_MAX_INJECT_TOKENS) {
115
+ const n = Number(env.HARNMEM_OPENCODE_MAX_INJECT_TOKENS);
116
+ if (Number.isFinite(n) && n > 0) settings.maxInjectTokens = Math.floor(n);
117
+ }
118
+
119
+ const home =
120
+ envHome && envHome.trim()
121
+ ? path.resolve(envHome)
122
+ : typeof settings.home === "string" && settings.home.trim()
123
+ ? path.resolve(expandHome(settings.home))
124
+ : baseHome;
125
+
126
+ return { ...settings, home };
127
+ }
128
+
129
+ export function debugEnabled(settings) {
130
+ return settings.debug === true;
131
+ }
package/package.json CHANGED
@@ -1,6 +1,26 @@
1
1
  {
2
2
  "name": "opencode-harnmem",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.1.0",
4
+ "private": false,
5
+ "type": "module",
6
+ "description": "opencode v2 plugin for HarnMem: byte-stable memory injection (session context hook), five native memory tools, and per-turn session journaling — in-process, zero runtime dependencies.",
7
+ "license": "MIT",
8
+ "files": [
9
+ "index.js",
10
+ "lib/",
11
+ "vendor/",
12
+ "README.md",
13
+ "LICENSE"
14
+ ],
15
+ "main": "index.js",
16
+ "devDependencies": {
17
+ "@harnmem/core": "0.4.0"
18
+ },
19
+ "engines": {
20
+ "node": ">=22.15"
21
+ },
22
+ "scripts": {
23
+ "build": "node scripts/vendor.mjs",
24
+ "test": "node --test test/*.test.mjs"
25
+ }
6
26
  }
@@ -0,0 +1,52 @@
1
+ export interface PromptAsset {
2
+ name: string;
3
+ version: number;
4
+ /** Stable system prompt (no placeholders). */
5
+ system: string;
6
+ /** User template with {{placeholders}}; null for legacy single-string prompts. */
7
+ user: string | null;
8
+ /** Where the asset was read from (diagnostics). */
9
+ source: string;
10
+ }
11
+ /** Repository/package root that contains `prompts/extract.md`, if discoverable. */
12
+ export declare function findAssetRoot(): string | null;
13
+ /** Read an asset, preferring the user's copy in <home>, then the shipped default. */
14
+ export declare function readAssetRaw(home: string, relativePath: string): {
15
+ text: string;
16
+ source: string;
17
+ } | null;
18
+ /**
19
+ * Synonym tables available to the engine: shipped defaults plus the user's
20
+ * editable copies, deduplicated by filename and sorted for deterministic
21
+ * loading. A language is added by adding `data/synonyms.<lang>.txt` — no code
22
+ * change; the `<home>` copy of a filename overrides the shipped default of the
23
+ * same name.
24
+ */
25
+ export declare function listSynonymFiles(home: string): string[];
26
+ /**
27
+ * Parse one synonym table. Shipped format is `canonical<TAB>syn1,syn2`;
28
+ * `=`, `:` and full-width `:` are accepted as separators too, and synonyms may
29
+ * be separated by `,`, `,` or `、`. Blank lines and `#` comments are ignored.
30
+ */
31
+ export declare function parseSynonymTable(raw: string): string[][];
32
+ /** All synonym groups from every discovered table; `files` is for diagnostics. */
33
+ export declare function loadSynonymGroups(home: string): {
34
+ groups: string[][];
35
+ files: string[];
36
+ };
37
+ /** Split a prompt file into stable system text + user template. */
38
+ export declare function parsePromptAsset(name: string, raw: string, source?: string): PromptAsset;
39
+ /** Load a prompt asset from <home>/prompts or the shipped defaults. */
40
+ export declare function loadPromptAsset(home: string, name: string): PromptAsset | null;
41
+ /** Replace `{{name}}` placeholders. Unknown placeholders are left in place on purpose. */
42
+ export declare function fillPrompt(template: string, vars: Record<string, string>): string;
43
+ export interface DeployResult {
44
+ copied: string[];
45
+ root: string | null;
46
+ }
47
+ /**
48
+ * Copy shipped defaults into <home>/ the first time they are needed. Existing
49
+ * user files are never overwritten. Returns copied paths (home-relative).
50
+ */
51
+ export declare function deployDefaultAssets(home: string): DeployResult;
52
+ //# sourceMappingURL=assets.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"assets.d.ts","sourceRoot":"","sources":["../src/assets.ts"],"names":[],"mappings":"AAoBA,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,8CAA8C;IAC9C,MAAM,EAAE,MAAM,CAAC;IACf,kFAAkF;IAClF,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,mDAAmD;IACnD,MAAM,EAAE,MAAM,CAAC;CAChB;AAID,mFAAmF;AACnF,wBAAgB,aAAa,IAAI,MAAM,GAAG,IAAI,CAwB7C;AAED,qFAAqF;AACrF,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAaxG;AAQD;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAevD;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,EAAE,CAazD;AAED,kFAAkF;AAClF,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG;IAAE,MAAM,EAAE,MAAM,EAAE,EAAE,CAAC;IAAC,KAAK,EAAE,MAAM,EAAE,CAAA;CAAE,CAQvF;AAED,mEAAmE;AACnE,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,SAAa,GAAG,WAAW,CAqC5F;AAED,uEAAuE;AACvE,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAI9E;AAED,0FAA0F;AAC1F,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAMjF;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CACrB;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,CA0B9D"}
@@ -0,0 +1,227 @@
1
+ /**
2
+ * Prompt/data asset loading + first-run deployment.
3
+ *
4
+ * Assets are plain files so users can edit them:
5
+ * <home>/prompts/*.md extraction / consolidation prompts
6
+ * <home>/data/*.txt tokenizer synonym tables
7
+ *
8
+ * Shipped defaults live at the repository (or package) root next to `packages/`,
9
+ * found by walking up from this module. When that root is unavailable (e.g. a
10
+ * bundled single-file build) callers fall back to the constants in extract.ts.
11
+ *
12
+ * Prompt file contract (see prompts/README.md): HTML comments are metadata and
13
+ * stripped at load time; a prompt file may contain `## system` and `## user`
14
+ * sections. Placeholders `{{name}}` are only allowed in the user section so the
15
+ * system prompt stays byte-stable for KV-cache reuse.
16
+ */
17
+ import { copyFileSync, existsSync, mkdirSync, readdirSync, readFileSync } from "node:fs";
18
+ import { dirname, join, resolve } from "node:path";
19
+ import { fileURLToPath } from "node:url";
20
+ let assetRootCache;
21
+ /** Repository/package root that contains `prompts/extract.md`, if discoverable. */
22
+ export function findAssetRoot() {
23
+ if (assetRootCache !== undefined)
24
+ return assetRootCache;
25
+ const candidates = [];
26
+ const env = process.env.HARNMEM_ASSETS_DIR;
27
+ if (env)
28
+ candidates.push(resolve(env));
29
+ try {
30
+ let dir = dirname(fileURLToPath(import.meta.url));
31
+ for (let i = 0; i < 8; i += 1) {
32
+ candidates.push(dir);
33
+ const parent = dirname(dir);
34
+ if (parent === dir)
35
+ break;
36
+ dir = parent;
37
+ }
38
+ }
39
+ catch {
40
+ // import.meta.url is always available in ESM; ignore exotic loaders.
41
+ }
42
+ for (const candidate of candidates) {
43
+ if (existsSync(join(candidate, "prompts", "extract.md"))) {
44
+ assetRootCache = candidate;
45
+ return candidate;
46
+ }
47
+ }
48
+ assetRootCache = null;
49
+ return null;
50
+ }
51
+ /** Read an asset, preferring the user's copy in <home>, then the shipped default. */
52
+ export function readAssetRaw(home, relativePath) {
53
+ const candidates = [join(home, relativePath)];
54
+ const root = findAssetRoot();
55
+ if (root)
56
+ candidates.push(join(root, relativePath));
57
+ for (const file of candidates) {
58
+ try {
59
+ const text = readFileSync(file, "utf8");
60
+ if (text.trim())
61
+ return { text, source: file };
62
+ }
63
+ catch {
64
+ // try next candidate
65
+ }
66
+ }
67
+ return null;
68
+ }
69
+ /**
70
+ * Language-tagged synonym tables: `synonyms.zh.txt`, `synonyms.en.txt`, ...;
71
+ * a bare `synonyms.txt` is accepted too.
72
+ */
73
+ const SYNONYM_FILE_RE = /^synonyms(?:\.[a-z0-9_-]+)?\.txt$/i;
74
+ /**
75
+ * Synonym tables available to the engine: shipped defaults plus the user's
76
+ * editable copies, deduplicated by filename and sorted for deterministic
77
+ * loading. A language is added by adding `data/synonyms.<lang>.txt` — no code
78
+ * change; the `<home>` copy of a filename overrides the shipped default of the
79
+ * same name.
80
+ */
81
+ export function listSynonymFiles(home) {
82
+ const names = new Set();
83
+ const collect = (root) => {
84
+ if (!root)
85
+ return;
86
+ try {
87
+ for (const file of readdirSync(join(root, "data"))) {
88
+ if (SYNONYM_FILE_RE.test(file))
89
+ names.add(file);
90
+ }
91
+ }
92
+ catch {
93
+ // optional directory
94
+ }
95
+ };
96
+ collect(home);
97
+ collect(findAssetRoot());
98
+ return [...names].sort();
99
+ }
100
+ /**
101
+ * Parse one synonym table. Shipped format is `canonical<TAB>syn1,syn2`;
102
+ * `=`, `:` and full-width `:` are accepted as separators too, and synonyms may
103
+ * be separated by `,`, `,` or `、`. Blank lines and `#` comments are ignored.
104
+ */
105
+ export function parseSynonymTable(raw) {
106
+ const groups = [];
107
+ for (const line of raw.split(/\r?\n/)) {
108
+ const trimmed = line.trim();
109
+ if (!trimmed || trimmed.startsWith("#"))
110
+ continue;
111
+ const match = /^([^\t=::]+)[\t=::]+(.+)$/.exec(trimmed);
112
+ if (!match)
113
+ continue;
114
+ const head = match[1];
115
+ const tail = match[2];
116
+ const group = [head, ...tail.split(/[,,、]/)].map((s) => s.trim()).filter(Boolean);
117
+ if (group.length >= 2)
118
+ groups.push(group);
119
+ }
120
+ return groups;
121
+ }
122
+ /** All synonym groups from every discovered table; `files` is for diagnostics. */
123
+ export function loadSynonymGroups(home) {
124
+ const files = listSynonymFiles(home);
125
+ const groups = [];
126
+ for (const file of files) {
127
+ const asset = readAssetRaw(home, join("data", file));
128
+ if (asset)
129
+ groups.push(...parseSynonymTable(asset.text));
130
+ }
131
+ return { groups, files };
132
+ }
133
+ /** Split a prompt file into stable system text + user template. */
134
+ export function parsePromptAsset(name, raw, source = "(inline)") {
135
+ const header = /<!--\s*version:\s*(\d+)\s*-->/i.exec(raw);
136
+ const version = header ? Number(header[1]) : 1;
137
+ const stripped = raw.replace(/<!--[\s\S]*?-->/g, "").replace(/\r\n/g, "\n");
138
+ const lines = stripped.split("\n");
139
+ const sections = {
140
+ system: [],
141
+ user: [],
142
+ current: "system",
143
+ };
144
+ const markers = [];
145
+ lines.forEach((line, index) => {
146
+ const match = /^\s*##\s+(system|user)\s*$/.exec(line);
147
+ if (match) {
148
+ markers.push({ at: index, kind: match[1] });
149
+ }
150
+ });
151
+ let system;
152
+ let user = null;
153
+ if (markers.length === 0) {
154
+ // Legacy custom prompt: whole file is the system prompt.
155
+ system = stripped.trim();
156
+ }
157
+ else {
158
+ const first = markers[0];
159
+ const body = lines.slice(0, first.at).join("\n").trim();
160
+ if (body)
161
+ sections.system.push(body);
162
+ for (let i = 0; i < markers.length; i += 1) {
163
+ const marker = markers[i];
164
+ const next = markers[i + 1];
165
+ const chunk = lines.slice(marker.at + 1, next ? next.at : lines.length).join("\n").trim();
166
+ if (marker.kind === "system")
167
+ sections.system.push(chunk);
168
+ else
169
+ sections.user.push(chunk);
170
+ }
171
+ system = sections.system.filter(Boolean).join("\n\n").trim();
172
+ user = sections.user.filter(Boolean).join("\n\n").trim() || null;
173
+ }
174
+ return { name, version, system, user, source };
175
+ }
176
+ /** Load a prompt asset from <home>/prompts or the shipped defaults. */
177
+ export function loadPromptAsset(home, name) {
178
+ const found = readAssetRaw(home, join("prompts", `${name}.md`));
179
+ if (!found)
180
+ return null;
181
+ return parsePromptAsset(name, found.text, found.source);
182
+ }
183
+ /** Replace `{{name}}` placeholders. Unknown placeholders are left in place on purpose. */
184
+ export function fillPrompt(template, vars) {
185
+ let out = template;
186
+ for (const [key, value] of Object.entries(vars)) {
187
+ out = out.split(`{{${key}}}`).join(value);
188
+ }
189
+ return out;
190
+ }
191
+ /**
192
+ * Copy shipped defaults into <home>/ the first time they are needed. Existing
193
+ * user files are never overwritten. Returns copied paths (home-relative).
194
+ */
195
+ export function deployDefaultAssets(home) {
196
+ const root = findAssetRoot();
197
+ if (!root)
198
+ return { copied: [], root: null };
199
+ const copied = [];
200
+ const copyTree = (relDir, filter) => {
201
+ const fromDir = join(root, relDir);
202
+ if (!existsSync(fromDir))
203
+ return;
204
+ const toDir = join(home, relDir);
205
+ mkdirSync(toDir, { recursive: true });
206
+ for (const file of readdirSync(fromDir)) {
207
+ if (!filter(file))
208
+ continue;
209
+ const from = join(fromDir, file);
210
+ const to = join(toDir, file);
211
+ if (existsSync(to))
212
+ continue;
213
+ try {
214
+ readFileSync(from); // existence/readability check
215
+ copyFileSync(from, to);
216
+ copied.push(join(relDir, file).replace(/\\/g, "/"));
217
+ }
218
+ catch {
219
+ // deployment is best-effort
220
+ }
221
+ }
222
+ };
223
+ copyTree("prompts", (file) => file.endsWith(".md") || file.endsWith(".txt"));
224
+ copyTree("data", (file) => file.endsWith(".txt") || file.endsWith(".tsv"));
225
+ return { copied, root };
226
+ }
227
+ //# sourceMappingURL=assets.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"assets.js","sourceRoot":"","sources":["../src/assets.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ import type { BudgetConfig, HarnMemConfig, GovernanceConfig, RetrievalConfig, WriteConfig } from "./types.js";
2
+ import { type FieldConfig } from "./text.js";
3
+ export declare const DEFAULT_BUDGET: BudgetConfig;
4
+ type FullRetrieval = RetrievalConfig;
5
+ type FullWrite = WriteConfig;
6
+ type FullGovernance = GovernanceConfig;
7
+ export declare const DEFAULT_RETRIEVAL: FullRetrieval;
8
+ export declare const DEFAULT_WRITE: FullWrite;
9
+ export declare const DEFAULT_GOVERNANCE: FullGovernance;
10
+ export interface Defaults {
11
+ home: string;
12
+ harness: string;
13
+ cwd: string;
14
+ now: () => number;
15
+ budget: BudgetConfig;
16
+ retrieval: FullRetrieval;
17
+ write: FullWrite;
18
+ governance: FullGovernance;
19
+ }
20
+ export declare const DEFAULT_CONFIG: Defaults;
21
+ export declare function mergeConfig(base: HarnMemConfig, override: HarnMemConfig | undefined): HarnMemConfig;
22
+ /**
23
+ * Load config from the home config file and the project-local `.harnmem.json`
24
+ * (if any), then apply explicit overrides.
25
+ */
26
+ export declare function loadConfig(home: string, projectRoot: string | undefined, explicit: HarnMemConfig | undefined): Promise<HarnMemConfig>;
27
+ export declare function fieldsOf(config: HarnMemConfig): FieldConfig;
28
+ /** Resolved ttl days for a type. */
29
+ export declare function ttlDays(config: HarnMemConfig, type: string): number;
30
+ /** Resolved retrieval config with all defaults applied. */
31
+ export declare function retrievalOf(config: HarnMemConfig): FullRetrieval;
32
+ export declare function writeOf(config: HarnMemConfig): FullWrite;
33
+ export declare function governanceOf(config: HarnMemConfig): FullGovernance;
34
+ export declare function budgetOf(config: HarnMemConfig): BudgetConfig;
35
+ export {};
36
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EACV,YAAY,EACZ,aAAa,EACb,gBAAgB,EAChB,eAAe,EACf,WAAW,EACZ,MAAM,YAAY,CAAC;AACpB,OAAO,EAAkB,KAAK,WAAW,EAAE,MAAM,WAAW,CAAC;AAG7D,eAAO,MAAM,cAAc,EAAE,YAK5B,CAAC;AAEF,KAAK,aAAa,GAAG,eAAe,CAAC;AACrC,KAAK,SAAS,GAAG,WAAW,CAAC;AAC7B,KAAK,cAAc,GAAG,gBAAgB,CAAC;AAEvC,eAAO,MAAM,iBAAiB,EAAE,aAe/B,CAAC;AAEF,eAAO,MAAM,aAAa,EAAE,SAQ3B,CAAC;AAEF,eAAO,MAAM,kBAAkB,EAAE,cAQhC,CAAC;AAEF,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,MAAM,CAAC;IAClB,MAAM,EAAE,YAAY,CAAC;IACrB,SAAS,EAAE,aAAa,CAAC;IACzB,KAAK,EAAE,SAAS,CAAC;IACjB,UAAU,EAAE,cAAc,CAAC;CAC5B;AAED,eAAO,MAAM,cAAc,EAAE,QAS5B,CAAC;AAEF,wBAAgB,WAAW,CAAC,IAAI,EAAE,aAAa,EAAE,QAAQ,EAAE,aAAa,GAAG,SAAS,GAAG,aAAa,CAcnG;AAYD;;;GAGG;AACH,wBAAsB,UAAU,CAC9B,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,MAAM,GAAG,SAAS,EAC/B,QAAQ,EAAE,aAAa,GAAG,SAAS,GAClC,OAAO,CAAC,aAAa,CAAC,CAWxB;AAED,wBAAgB,QAAQ,CAAC,MAAM,EAAE,aAAa,GAAG,WAAW,CAE3D;AAED,oCAAoC;AACpC,wBAAgB,OAAO,CAAC,MAAM,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAInE;AAED,2DAA2D;AAC3D,wBAAgB,WAAW,CAAC,MAAM,EAAE,aAAa,GAAG,aAAa,CAEhE;AAED,wBAAgB,OAAO,CAAC,MAAM,EAAE,aAAa,GAAG,SAAS,CAExD;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,aAAa,GAAG,cAAc,CAElE;AAED,wBAAgB,QAAQ,CAAC,MAAM,EAAE,aAAa,GAAG,YAAY,CAE5D"}