@gamaze/hicortex 0.7.1 → 0.10.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 (49) hide show
  1. package/README.md +57 -39
  2. package/dist/claude-md.d.ts +9 -21
  3. package/dist/claude-md.js +9 -241
  4. package/dist/cli.d.ts +3 -2
  5. package/dist/cli.js +29 -11
  6. package/dist/consolidate.js +0 -7
  7. package/dist/db.js +24 -0
  8. package/dist/embedder.d.ts +11 -0
  9. package/dist/embedder.js +27 -0
  10. package/dist/extensions.d.ts +41 -88
  11. package/dist/extensions.js +36 -61
  12. package/dist/features.d.ts +21 -25
  13. package/dist/features.js +47 -83
  14. package/dist/hermes-transcript-reader.d.ts +27 -0
  15. package/dist/hermes-transcript-reader.js +134 -0
  16. package/dist/index.d.ts +16 -4
  17. package/dist/index.js +252 -344
  18. package/dist/init.d.ts +41 -1
  19. package/dist/init.js +545 -190
  20. package/dist/lesson-selection.d.ts +62 -0
  21. package/dist/lesson-selection.js +159 -0
  22. package/dist/lessons-context.d.ts +17 -0
  23. package/dist/lessons-context.js +96 -0
  24. package/dist/llm.d.ts +42 -29
  25. package/dist/llm.js +89 -270
  26. package/dist/mcp-server.d.ts +0 -1
  27. package/dist/mcp-server.js +404 -86
  28. package/dist/nightly.d.ts +9 -6
  29. package/dist/nightly.js +197 -357
  30. package/dist/oc-transcript-reader.d.ts +20 -0
  31. package/dist/oc-transcript-reader.js +61 -0
  32. package/dist/pi-transcript-reader.d.ts +1 -0
  33. package/dist/status.js +22 -2
  34. package/dist/storage.d.ts +7 -1
  35. package/dist/storage.js +28 -7
  36. package/dist/transcript-reader.d.ts +19 -0
  37. package/dist/transcript-reader.js +17 -3
  38. package/dist/types.d.ts +10 -0
  39. package/dist/uninstall.js +31 -1
  40. package/hermes-plugin/hicortex/README.md +77 -0
  41. package/hermes-plugin/hicortex/__init__.py +17 -0
  42. package/hermes-plugin/hicortex/client.py +162 -0
  43. package/hermes-plugin/hicortex/config.py +105 -0
  44. package/hermes-plugin/hicortex/plugin.yaml +12 -0
  45. package/hermes-plugin/hicortex/provider.py +432 -0
  46. package/openclaw.plugin.json +17 -44
  47. package/package.json +7 -5
  48. package/dist/pro-loader.d.ts +0 -33
  49. package/dist/pro-loader.js +0 -187
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Domain-aware lesson selection engine — the DEFAULT selector for all installs.
3
+ *
4
+ * History: originally shipped as a Pro feature (src/pro/selection.ts), deleted
5
+ * with the Pro loader in #122, restored into core in #123. Free personal
6
+ * self-host = the full product; there is no gated variant of this selector.
7
+ *
8
+ * Behaviour: score each lesson by four axes, pick the top N, dedup
9
+ * semantically-adjacent entries by normalized-prefix matching.
10
+ *
11
+ * Scoring formula:
12
+ * score = w_project * project_match
13
+ * + w_recency * recency_score
14
+ * + w_strength * base_strength
15
+ * + w_access * access_affinity
16
+ *
17
+ * where:
18
+ * project_match:
19
+ * 1.0 if lesson.project === ctx.project
20
+ * 0.5 if lesson.project is in the same knowledge domain (via ctx.moduleIndex)
21
+ * 0.3 if lesson.project === "global"
22
+ * 0.0 otherwise
23
+ *
24
+ * recency_score:
25
+ * exp(-age_days / half_life_days) in [0, 1]
26
+ * half_life_days = 30 — lessons fade to half weight after 30 days
27
+ *
28
+ * base_strength:
29
+ * lesson.base_strength from the DB (already in [0, 1])
30
+ * represents the importance score from the nightly reflection stage
31
+ *
32
+ * access_affinity:
33
+ * min(access_count / 5, 1.0) — capped at 5 accesses
34
+ * lessons that have been retrieved before get a small boost
35
+ *
36
+ * Default weights:
37
+ * w_project = 0.40 (in-project relevance matters most)
38
+ * w_recency = 0.25 (newer lessons preferred)
39
+ * w_strength = 0.25 (important lessons preferred)
40
+ * w_access = 0.10 (proven-useful lessons preferred)
41
+ *
42
+ * After scoring, lessons are sorted high-to-low, then deduplicated via
43
+ * prefix matching: a lesson is considered a duplicate of an already-selected
44
+ * lesson if its normalized content is a prefix of the other (or vice versa),
45
+ * with at least 30 characters in common. This catches lessons that were
46
+ * rewritten slightly across nightly runs — e.g. "Always validate input" vs
47
+ * "Always validate input before processing any data". The 30-char floor
48
+ * prevents false positives on genuinely short, distinct lessons.
49
+ *
50
+ * When all lessons score identically (no project/date/strength metadata),
51
+ * the sort is stable, so input order is preserved — equivalent to the old
52
+ * slice(0, N) behaviour for metadata-free candidate pools.
53
+ *
54
+ * Generic over T so the same function handles:
55
+ * - Memory[] from local DB queries (server mode)
56
+ * - {content, created_at, base_strength, access_count} HTTP shape (client mode)
57
+ */
58
+ import type { LessonSelector } from "./extensions.js";
59
+ /**
60
+ * The domain-aware lesson selector. Registered as the default in extensions.ts.
61
+ */
62
+ export declare const domainAwareLessonSelector: LessonSelector;
@@ -0,0 +1,159 @@
1
+ "use strict";
2
+ /**
3
+ * Domain-aware lesson selection engine — the DEFAULT selector for all installs.
4
+ *
5
+ * History: originally shipped as a Pro feature (src/pro/selection.ts), deleted
6
+ * with the Pro loader in #122, restored into core in #123. Free personal
7
+ * self-host = the full product; there is no gated variant of this selector.
8
+ *
9
+ * Behaviour: score each lesson by four axes, pick the top N, dedup
10
+ * semantically-adjacent entries by normalized-prefix matching.
11
+ *
12
+ * Scoring formula:
13
+ * score = w_project * project_match
14
+ * + w_recency * recency_score
15
+ * + w_strength * base_strength
16
+ * + w_access * access_affinity
17
+ *
18
+ * where:
19
+ * project_match:
20
+ * 1.0 if lesson.project === ctx.project
21
+ * 0.5 if lesson.project is in the same knowledge domain (via ctx.moduleIndex)
22
+ * 0.3 if lesson.project === "global"
23
+ * 0.0 otherwise
24
+ *
25
+ * recency_score:
26
+ * exp(-age_days / half_life_days) in [0, 1]
27
+ * half_life_days = 30 — lessons fade to half weight after 30 days
28
+ *
29
+ * base_strength:
30
+ * lesson.base_strength from the DB (already in [0, 1])
31
+ * represents the importance score from the nightly reflection stage
32
+ *
33
+ * access_affinity:
34
+ * min(access_count / 5, 1.0) — capped at 5 accesses
35
+ * lessons that have been retrieved before get a small boost
36
+ *
37
+ * Default weights:
38
+ * w_project = 0.40 (in-project relevance matters most)
39
+ * w_recency = 0.25 (newer lessons preferred)
40
+ * w_strength = 0.25 (important lessons preferred)
41
+ * w_access = 0.10 (proven-useful lessons preferred)
42
+ *
43
+ * After scoring, lessons are sorted high-to-low, then deduplicated via
44
+ * prefix matching: a lesson is considered a duplicate of an already-selected
45
+ * lesson if its normalized content is a prefix of the other (or vice versa),
46
+ * with at least 30 characters in common. This catches lessons that were
47
+ * rewritten slightly across nightly runs — e.g. "Always validate input" vs
48
+ * "Always validate input before processing any data". The 30-char floor
49
+ * prevents false positives on genuinely short, distinct lessons.
50
+ *
51
+ * When all lessons score identically (no project/date/strength metadata),
52
+ * the sort is stable, so input order is preserved — equivalent to the old
53
+ * slice(0, N) behaviour for metadata-free candidate pools.
54
+ *
55
+ * Generic over T so the same function handles:
56
+ * - Memory[] from local DB queries (server mode)
57
+ * - {content, created_at, base_strength, access_count} HTTP shape (client mode)
58
+ */
59
+ Object.defineProperty(exports, "__esModule", { value: true });
60
+ exports.domainAwareLessonSelector = void 0;
61
+ const W_PROJECT = 0.40;
62
+ const W_RECENCY = 0.25;
63
+ const W_STRENGTH = 0.25;
64
+ const W_ACCESS = 0.10;
65
+ const RECENCY_HALF_LIFE_DAYS = 30;
66
+ function parseDate(ts) {
67
+ if (!ts)
68
+ return null;
69
+ const d = new Date(ts);
70
+ return isNaN(d.getTime()) ? null : d;
71
+ }
72
+ function projectMatch(lesson, targetProject, moduleIndex) {
73
+ if (!lesson.project)
74
+ return 0.0;
75
+ if (targetProject && lesson.project === targetProject)
76
+ return 1.0;
77
+ if (lesson.project === "global")
78
+ return 0.3;
79
+ // Domain-aware: same domain = 0.5
80
+ if (targetProject && moduleIndex) {
81
+ const targetDomain = moduleIndex.domains.find((d) => d.projects.includes(targetProject));
82
+ if (targetDomain && targetDomain.projects.includes(lesson.project)) {
83
+ return 0.5;
84
+ }
85
+ }
86
+ return 0.0;
87
+ }
88
+ function recencyScore(lesson, now) {
89
+ const created = parseDate(lesson.created_at);
90
+ if (!created)
91
+ return 0.5; // unknown date → neutral score
92
+ const ageMs = now.getTime() - created.getTime();
93
+ const ageDays = ageMs / (1000 * 60 * 60 * 24);
94
+ if (ageDays < 0)
95
+ return 1.0; // future-dated, treat as very recent
96
+ return Math.exp(-ageDays / RECENCY_HALF_LIFE_DAYS);
97
+ }
98
+ function accessAffinity(lesson) {
99
+ const count = lesson.access_count ?? 0;
100
+ return Math.min(count / 5, 1.0);
101
+ }
102
+ /**
103
+ * Normalize a lesson's content to its canonical form for prefix comparison.
104
+ * Lowercase, collapse whitespace, trim. Full content is preserved so prefix
105
+ * matching below can handle lessons of different lengths.
106
+ */
107
+ function dedupKey(lesson) {
108
+ return lesson.content.toLowerCase().replace(/\s+/g, " ").trim();
109
+ }
110
+ /**
111
+ * Minimum shared-prefix length for two lessons to be considered duplicates.
112
+ * Shorter values cause false positives on genuinely distinct short lessons;
113
+ * longer values miss legitimate duplicates that happen to differ in the
114
+ * first few dozen characters.
115
+ */
116
+ const DEDUP_MIN_SHARED_PREFIX = 30;
117
+ function isPrefixDuplicate(candidate, existing) {
118
+ const shorter = candidate.length < existing.length ? candidate : existing;
119
+ const longer = candidate.length < existing.length ? existing : candidate;
120
+ return shorter.length >= DEDUP_MIN_SHARED_PREFIX && longer.startsWith(shorter);
121
+ }
122
+ /**
123
+ * The domain-aware lesson selector. Registered as the default in extensions.ts.
124
+ */
125
+ exports.domainAwareLessonSelector = {
126
+ select(lessons, ctx) {
127
+ if (lessons.length === 0)
128
+ return [];
129
+ const now = new Date();
130
+ // Score every lesson
131
+ const scored = lessons.map((lesson) => {
132
+ const pMatch = projectMatch(lesson, ctx.project, ctx.moduleIndex);
133
+ const rScore = recencyScore(lesson, now);
134
+ const sScore = lesson.base_strength ?? 0.5;
135
+ const aScore = accessAffinity(lesson);
136
+ const score = W_PROJECT * pMatch +
137
+ W_RECENCY * rScore +
138
+ W_STRENGTH * sScore +
139
+ W_ACCESS * aScore;
140
+ return { lesson, score };
141
+ });
142
+ // Sort high → low (Array.prototype.sort is stable — equal scores keep input order)
143
+ scored.sort((a, b) => b.score - a.score);
144
+ // Select top N with prefix-based dedup
145
+ const selected = [];
146
+ const selectedKeys = [];
147
+ for (const { lesson } of scored) {
148
+ if (selected.length >= ctx.maxLessons)
149
+ break;
150
+ const key = dedupKey(lesson);
151
+ const isDup = selectedKeys.some((existing) => isPrefixDuplicate(key, existing));
152
+ if (isDup)
153
+ continue;
154
+ selectedKeys.push(key);
155
+ selected.push(lesson);
156
+ }
157
+ return selected;
158
+ },
159
+ };
@@ -0,0 +1,17 @@
1
+ /**
2
+ * lessons-context — query-time lessons fetch for the CC SessionStart hook.
3
+ *
4
+ * Replaces file-based injection (injectLessons / injectLessonsFromServer).
5
+ * Reads ~/.hicortex/config.json to find the server URL, GETs /lessons,
6
+ * and prints a compact Markdown block to stdout so CC picks it up as
7
+ * session context.
8
+ *
9
+ * Fail-soft by design: ANY failure (missing config, network error, non-2xx,
10
+ * parse error) results in silent exit-0. A broken hook must never block a
11
+ * CC session.
12
+ */
13
+ /**
14
+ * Fetch lessons from the configured server and return a formatted Markdown
15
+ * block, or null on any failure (caller should print nothing and exit 0).
16
+ */
17
+ export declare function fetchLessonsContext(): Promise<string | null>;
@@ -0,0 +1,96 @@
1
+ "use strict";
2
+ /**
3
+ * lessons-context — query-time lessons fetch for the CC SessionStart hook.
4
+ *
5
+ * Replaces file-based injection (injectLessons / injectLessonsFromServer).
6
+ * Reads ~/.hicortex/config.json to find the server URL, GETs /lessons,
7
+ * and prints a compact Markdown block to stdout so CC picks it up as
8
+ * session context.
9
+ *
10
+ * Fail-soft by design: ANY failure (missing config, network error, non-2xx,
11
+ * parse error) results in silent exit-0. A broken hook must never block a
12
+ * CC session.
13
+ */
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.fetchLessonsContext = fetchLessonsContext;
16
+ const node_fs_1 = require("node:fs");
17
+ const node_path_1 = require("node:path");
18
+ const node_os_1 = require("node:os");
19
+ const features_js_1 = require("./features.js");
20
+ const extensions_js_1 = require("./extensions.js");
21
+ const state_js_1 = require("./state.js");
22
+ const HICORTEX_HOME = (0, node_path_1.join)((0, node_os_1.homedir)(), ".hicortex");
23
+ const DEFAULT_PORT = 8787;
24
+ /**
25
+ * Fetch lessons from the configured server and return a formatted Markdown
26
+ * block, or null on any failure (caller should print nothing and exit 0).
27
+ */
28
+ async function fetchLessonsContext() {
29
+ let config = {};
30
+ try {
31
+ config = JSON.parse((0, node_fs_1.readFileSync)((0, node_path_1.join)(HICORTEX_HOME, "config.json"), "utf-8"));
32
+ }
33
+ catch {
34
+ // No config file — server not set up yet. Fail soft.
35
+ return null;
36
+ }
37
+ // Determine server URL: client mode uses serverUrl; server mode uses localhost.
38
+ const serverUrl = config.mode === "client" && typeof config.serverUrl === "string"
39
+ ? config.serverUrl.replace(/\/+$/, "")
40
+ : `http://127.0.0.1:${config.port ?? DEFAULT_PORT}`;
41
+ const authToken = config.authToken;
42
+ let data;
43
+ try {
44
+ const resp = await fetch(`${serverUrl}/lessons`, {
45
+ headers: authToken ? { "Authorization": `Bearer ${authToken}` } : {},
46
+ signal: AbortSignal.timeout(3000),
47
+ });
48
+ if (!resp.ok)
49
+ return null;
50
+ data = await resp.json();
51
+ }
52
+ catch {
53
+ return null;
54
+ }
55
+ const maxLessons = (0, features_js_1.lessonsLimit)();
56
+ const moduleIndex = data.moduleIndex ?? (0, state_js_1.loadState)(HICORTEX_HOME).moduleIndex;
57
+ // The SessionStart hook runs in the session's working directory, whose last
58
+ // path component matches the capture-side CC project name convention
59
+ // (transcript-reader's decodeProjectDirName also takes the last component).
60
+ // This enables in-project + same-domain lesson boosting on the CC path.
61
+ const project = (0, node_path_1.basename)(process.cwd()) || null;
62
+ const selected = await (0, extensions_js_1.getLessonSelector)().select(data.lessons, { maxLessons, moduleIndex, project });
63
+ const lessonLines = selected.map((l) => {
64
+ const titleMatch = l.content.match(/## Lesson: (.+)/);
65
+ const typeMatch = l.content.match(/\*\*Type:\*\* (\w+)/);
66
+ const severityMatch = l.content.match(/\*\*Severity:\*\* (\w+)/);
67
+ const title = titleMatch ? titleMatch[1] : l.content.slice(0, 150);
68
+ const meta = [severityMatch?.[1], typeMatch?.[1]].filter(Boolean).join(", ");
69
+ return `- ${title}${meta ? ` (${meta})` : ""}`;
70
+ });
71
+ const parts = ["## Hicortex Memory", ""];
72
+ parts.push("You have access to shared long-term memory across all agents and sessions.");
73
+ parts.push("BEFORE making decisions, search memory: `hicortex_search` for prior decisions on the same topic.");
74
+ parts.push("Use `hicortex_context` at session start for recent project state.");
75
+ if (lessonLines.length > 0) {
76
+ parts.push("", "### Lessons (updated nightly)");
77
+ parts.push(...lessonLines);
78
+ }
79
+ // Memory index
80
+ const { index } = data;
81
+ if (moduleIndex && moduleIndex.domains.length > 0) {
82
+ parts.push("", "### Memory Index");
83
+ for (const domain of moduleIndex.domains) {
84
+ const kwStr = domain.keywords.length > 0 ? `: ${domain.keywords.join(", ")}` : "";
85
+ parts.push(`${domain.name} (${domain.memoryCount} memories, ${domain.lessonCount} lessons)${kwStr}`);
86
+ parts.push(` ${domain.projects.join(" | ")}`);
87
+ }
88
+ parts.push(`${index.total} memories, ${index.lessonCount} lessons, ${index.sourceCount} agents. Search with \`hicortex_search\`.`);
89
+ }
90
+ else if (index.projects.length > 0) {
91
+ parts.push("", "### Memory Index");
92
+ parts.push(index.projects.map(p => `${p.name}: ${p.count}`).join(" | "));
93
+ parts.push(`${index.total} memories, ${index.lessonCount} lessons, ${index.sourceCount} agents. Search with \`hicortex_search\`.`);
94
+ }
95
+ return parts.join("\n");
96
+ }
package/dist/llm.d.ts CHANGED
@@ -1,19 +1,14 @@
1
1
  /**
2
2
  * Multi-provider LLM client for consolidation and distillation.
3
3
  *
4
- * Resolution for OC adapter (resolveLlmConfig):
5
- * 1. Plugin config (llmBaseUrl, llmApiKey, llmModel)
6
- * 2. ~/.openclaw/openclaw.json agents.defaults.model.primary
7
- * 3. Environment vars: OPENAI_API_KEY, ANTHROPIC_API_KEY, GOOGLE_API_KEY
8
- * 4. Fallback: Ollama at http://localhost:11434
4
+ * Resolution (resolveExplicitLlmConfig):
5
+ * 1. Explicit config-file overrides (llmBaseUrl + llmApiKey + llmModel)
6
+ * 2. Hicortex-specific env vars (HICORTEX_LLM_BASE_URL + HICORTEX_LLM_API_KEY + HICORTEX_LLM_MODEL)
7
+ * Returns null when nothing explicit is set — no silent defaults.
9
8
  *
10
- * Resolution for CC adapter (resolveLlmConfigForCC):
11
- * 1. Explicit env vars (HICORTEX_LLM_BASE_URL + HICORTEX_LLM_API_KEY + HICORTEX_LLM_MODEL)
12
- * 2. ANTHROPIC_API_KEYClaude Haiku (cheap, CC users always have this)
13
- * 3. OPENAI_API_KEY → gpt-5.4-nano
14
- * 4. GOOGLE_API_KEY → gemini-2.5-flash
15
- * 5. Claude CLI fallback (uses subscription, no API key needed)
16
- * 6. Fallback: Ollama at http://localhost:11434
9
+ * Explicit backends (handled by call sites before resolveExplicitLlmConfig):
10
+ * - llmBackend: "claude-cli" claudeCliConfig()
11
+ * - llmBackend: "ollama" explicit ollama LlmConfig
17
12
  *
18
13
  * Supports any OpenAI-compatible endpoint plus first-class support for
19
14
  * OpenAI, Anthropic, Google, Ollama, OpenRouter, and Claude CLI.
@@ -36,25 +31,28 @@ export interface LlmConfig {
36
31
  reflectProvider?: string;
37
32
  }
38
33
  /**
39
- * Resolve LLM configuration from plugin config, OpenClaw config, env vars, or Ollama fallback.
34
+ * Resolve LLM configuration from explicit config-file overrides or
35
+ * Hicortex-specific env vars only. Returns null when nothing explicit is set.
36
+ *
37
+ * Call sites (mcp-server, nightly) handle the named backends (claude-cli,
38
+ * ollama) before reaching this function. Only call this for the "other
39
+ * provider" / API-key case where no named backend is in config.json.
40
+ *
41
+ * NO implicit fallbacks: ANTHROPIC_API_KEY, OPENAI_API_KEY, GOOGLE_API_KEY
42
+ * alone in the environment do NOT configure an LLM — the user must have
43
+ * chosen a provider via `npx @gamaze/hicortex init`.
40
44
  */
41
- export declare function resolveLlmConfig(pluginConfig?: {
45
+ export declare function resolveExplicitLlmConfig(overrides?: {
42
46
  llmBaseUrl?: string;
43
47
  llmApiKey?: string;
44
48
  llmModel?: string;
45
49
  reflectModel?: string;
46
- }): LlmConfig;
50
+ }): LlmConfig | null;
47
51
  /**
48
- * Resolve LLM configuration for Claude Code (no OC config file).
49
- * Uses env vars only CC users always have ANTHROPIC_API_KEY.
50
- * Defaults to Haiku for distillation/scoring (~$0.50/mo).
52
+ * @deprecated Use resolveExplicitLlmConfig. This alias exists only to ease
53
+ * the transition for any lingering call sites remove after 0.10.0 ships.
51
54
  */
52
- export declare function resolveLlmConfigForCC(overrides?: {
53
- llmBaseUrl?: string;
54
- llmApiKey?: string;
55
- llmModel?: string;
56
- reflectModel?: string;
57
- }): LlmConfig;
55
+ export declare const resolveLlmConfigForCC: typeof resolveExplicitLlmConfig;
58
56
  /**
59
57
  * Find the claude CLI binary. Returns the full path or null.
60
58
  */
@@ -78,7 +76,7 @@ export declare function probeOllama(baseUrl?: string): Promise<string | null>;
78
76
  * - `ok: false, reason: "unreachable"` — network failure or non-2xx.
79
77
  * - `ok: false, reason: "model_missing"` — endpoint is up but the
80
78
  * model isn't listed (the exact case that caused data loss when
81
- * mhac-pro's Ollama didn't have the distill model loaded).
79
+ * a remote Ollama box didn't have the distill model loaded).
82
80
  *
83
81
  * Matches on exact name OR name prefix ("qwen3.5:35b" matches "qwen3.5:35b-a3b").
84
82
  */
@@ -89,11 +87,26 @@ export declare function probeOllamaModel(baseUrl: string, modelName: string): Pr
89
87
  reason: "unreachable" | "model_missing";
90
88
  }>;
91
89
  /**
92
- * For batch operations (nightly pipeline), prefer Ollama when available.
93
- * Claude CLI has strict rate limits that kill batch distillation.
94
- * Falls back to the provided config if Ollama is unreachable.
90
+ * Resolve the distillation endpoint before a /distill request.
91
+ *
92
+ * @param config LlmConfig (mutated in "local" mode when fallback is used)
93
+ * @param mode
94
+ * "strict" (default) — when a separate distillBaseUrl is configured and its
95
+ * Ollama probe fails, return "abort" immediately WITHOUT mutating config.
96
+ * The session is not distilled now; the nightly watermark is not advanced,
97
+ * so the session is re-shipped on the next run (harness stores retain raw
98
+ * for 30–90 days — the retry IS the queue). Prefer this to producing
99
+ * low-quality memories from a weak fallback model.
100
+ * "local" — legacy 0.9.0 behaviour: fall back to the base endpoint (local
101
+ * Ollama or API provider) when the remote is down. Mutates config IN PLACE
102
+ * to repoint distill* at the fallback.
103
+ *
104
+ * Returns:
105
+ * "ok" — remote distill endpoint healthy, or no separate endpoint set
106
+ * "fellback" — ("local" mode only) remote down; distill redirected to base
107
+ * "abort" — remote down and fallback not allowed (strict) or both down (local)
95
108
  */
96
- export declare function preferOllamaForBatch(resolved: LlmConfig, ollamaBaseUrl?: string): Promise<LlmConfig>;
109
+ export declare function resolveDistillFallback(config: LlmConfig, mode?: "strict" | "local"): Promise<"ok" | "fellback" | "abort">;
97
110
  export declare class RateLimitError extends Error {
98
111
  retryAfterMs: number;
99
112
  constructor(retryAfterMs: number);