@bcmyguest/openmem 0.0.0-stage → 0.6.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.
- package/LICENSE +190 -0
- package/README.md +136 -2
- package/bin/hindsight-project-index +530 -0
- package/cli/coding-agent/access-policy.mjs +38 -0
- package/cli/coding-agent/agents.mjs +34 -0
- package/cli/coding-agent/bank-from-cwd.mjs +140 -0
- package/cli/coding-agent/capabilities.mjs +23 -0
- package/cli/coding-agent/hook-harnesses.mjs +87 -0
- package/cli/coding-agent/hook-runner.mjs +260 -0
- package/cli/coding-agent/install-access.mjs +93 -0
- package/cli/coding-agent/install-shim.mjs +97 -0
- package/cli/coding-agent/opencode-config.mjs +91 -0
- package/cli/coding-agent/opencode-plugin.mjs +96 -0
- package/cli/coding-agent/runtime.mjs +39 -0
- package/cli/coding-agent/session-facade.mjs +62 -0
- package/cli/cognee-mcp.mjs +260 -0
- package/cli/feed-sync.mjs +11 -0
- package/cli/install-coding-agent.mjs +165 -0
- package/cli/install-memory-mcp.mjs +426 -0
- package/cli/is-main.mjs +18 -0
- package/cli/mcp/context.mjs +61 -0
- package/cli/mcp/export/cognee-dataset-name.mjs +12 -0
- package/cli/mcp/export/cognee.mjs +119 -0
- package/cli/mcp/export/hindsight.mjs +48 -0
- package/cli/mcp/export/index.mjs +26 -0
- package/cli/mcp/export/mem0.mjs +31 -0
- package/cli/mcp/llm-synthesize.mjs +50 -0
- package/cli/mcp/mem0-scope-id.mjs +22 -0
- package/cli/mcp/memory-facade.mjs +455 -0
- package/cli/mcp/memory-reflect-consolidate.mjs +120 -0
- package/cli/mcp/notebooklm-banks.mjs +21 -0
- package/cli/mcp/notebooklm-hindsight-rest.mjs +45 -0
- package/cli/mcp/notebooklm-provider.mjs +225 -0
- package/cli/mcp/pmm-records.mjs +23 -0
- package/cli/mcp/portability.mjs +158 -0
- package/cli/mcp/stdio.mjs +65 -0
- package/cli/mcp/unified-mcp-install.mjs +65 -0
- package/cli/mem0-mcp.mjs +108 -0
- package/cli/notebooklm-mcp.mjs +236 -0
- package/cli/openmem-mcp.mjs +35 -0
- package/cli/openmem.mjs +205 -0
- package/cli/roots.mjs +85 -0
- package/cli/stack.mjs +436 -0
- package/cli/switch-memory-provider.mjs +358 -0
- package/cli/wiring-report.mjs +125 -0
- package/compose.bench.yaml +12 -0
- package/compose.yaml +164 -0
- package/config/coding-agent.json +13 -0
- package/config/hindsight.env.example +24 -0
- package/config/index.yml +54 -0
- package/config/litellm.lemond.yaml +40 -0
- package/config/litellm.ollama.yaml +33 -0
- package/config/notebooklm.yml +33 -0
- package/config/openmem.toml.example +62 -0
- package/docs/OPENMEM.md +23 -0
- package/docs/architecture.md +219 -0
- package/docs/assets/README.md +10 -0
- package/docs/assets/setup-demo.gif +0 -0
- package/docs/coding-agent-hooks.md +51 -0
- package/docs/coding-agent-scopes.md +22 -0
- package/docs/coding-agents.md +51 -0
- package/docs/cognee.md +37 -0
- package/docs/concepts.md +112 -0
- package/docs/hindsight.md +74 -0
- package/docs/index-sources-roadmap.md +38 -0
- package/docs/index-sources.md +39 -0
- package/docs/indexer-sinks.md +122 -0
- package/docs/indexing.md +57 -0
- package/docs/manual-stack-setup.md +74 -0
- package/docs/mcp-install-guide.md +4 -0
- package/docs/mcp-setup.md +101 -0
- package/docs/mem0.md +103 -0
- package/docs/memory-portability.md +73 -0
- package/docs/memory-providers.md +246 -0
- package/docs/model-configuration.md +84 -0
- package/docs/notebooklm.md +83 -0
- package/docs/npx-install.md +170 -0
- package/docs/openmem-config.md +194 -0
- package/docs/provider-wizard-hooks.md +222 -0
- package/docs/quickstart.md +101 -0
- package/docs/setup-wizard.md +116 -0
- package/docs/sources-feeds.md +52 -0
- package/docs/sources-readwise.md +23 -0
- package/docs/sources-zotero.md +22 -0
- package/glama.json +4 -0
- package/indexer/Dockerfile +38 -0
- package/indexer/code_outline.py +146 -0
- package/indexer/feed_sync.py +107 -0
- package/indexer/index_config.py +290 -0
- package/indexer/pytest.ini +5 -0
- package/indexer/sinks/__init__.py +148 -0
- package/indexer/sinks/_http.py +44 -0
- package/indexer/sinks/cognee.py +329 -0
- package/indexer/sinks/hindsight.py +107 -0
- package/indexer/sinks/mem0.py +247 -0
- package/indexer/watch.py +240 -0
- package/package.json +95 -4
- package/providers/_template/compose.yaml +52 -0
- package/providers/cognee/OPERATOR.md +82 -0
- package/providers/cognee/compose.yaml +89 -0
- package/providers/cognee/lib/clack.mjs +157 -0
- package/providers/cognee/lib/coding-agent.mjs +26 -0
- package/providers/cognee/lib/cognee-mcp-install.mjs +153 -0
- package/providers/cognee/lib/config.mjs +98 -0
- package/providers/cognee/lib/env-file.mjs +194 -0
- package/providers/cognee/lib/env-setup.mjs +83 -0
- package/providers/cognee/lib/llm-env.mjs +382 -0
- package/providers/cognee/lib/ports.mjs +25 -0
- package/providers/cognee/lib/smoke.mjs +246 -0
- package/providers/cognee/lib/stack-env.mjs +134 -0
- package/providers/cognee/lib/state.mjs +101 -0
- package/providers/cognee/lib/summary.mjs +56 -0
- package/providers/cognee/lib/validate.mjs +97 -0
- package/providers/cognee/manifest.json +56 -0
- package/providers/cognee/wizard.mjs +134 -0
- package/providers/graphiti/manifest.json +15 -0
- package/providers/hindsight/lib/clack.mjs +245 -0
- package/providers/hindsight/lib/coding-agent.mjs +204 -0
- package/providers/hindsight/lib/config.mjs +73 -0
- package/providers/hindsight/lib/env-setup.mjs +71 -0
- package/providers/hindsight/lib/litellm-routes.mjs +187 -0
- package/providers/hindsight/lib/model-backend.mjs +214 -0
- package/providers/hindsight/lib/notebooklm-mcp-install.mjs +127 -0
- package/providers/hindsight/lib/ollama-compose.mjs +51 -0
- package/providers/hindsight/lib/ports.mjs +30 -0
- package/providers/hindsight/lib/probe.mjs +33 -0
- package/providers/hindsight/lib/stack-env.mjs +132 -0
- package/providers/hindsight/lib/state.mjs +75 -0
- package/providers/hindsight/lib/summary.mjs +43 -0
- package/providers/hindsight/lib/validate.mjs +75 -0
- package/providers/hindsight/manifest.json +58 -0
- package/providers/hindsight/wizard.mjs +149 -0
- package/providers/letta/manifest.json +29 -0
- package/providers/mem0/OPERATOR.md +124 -0
- package/providers/mem0/compose.yaml +57 -0
- package/providers/mem0/lib/coding-agent.mjs +58 -0
- package/providers/mem0/lib/config.mjs +45 -0
- package/providers/mem0/lib/configure.mjs +135 -0
- package/providers/mem0/lib/dotenv.mjs +71 -0
- package/providers/mem0/lib/env-file.mjs +110 -0
- package/providers/mem0/lib/http.mjs +69 -0
- package/providers/mem0/lib/mcp-install.mjs +165 -0
- package/providers/mem0/lib/secret.mjs +30 -0
- package/providers/mem0/lib/smoke.mjs +179 -0
- package/providers/mem0/lib/stack-env.mjs +216 -0
- package/providers/mem0/lib/state.mjs +67 -0
- package/providers/mem0/lib/summary.mjs +60 -0
- package/providers/mem0/lib/url.mjs +106 -0
- package/providers/mem0/lib/validate.mjs +158 -0
- package/providers/mem0/manifest.json +47 -0
- package/providers/mem0/wizard.mjs +133 -0
- package/providers/zep/manifest.json +29 -0
- package/scripts/check-pack.mjs +218 -0
- package/scripts/gen-masks.mjs +83 -0
- package/scripts/gen-masks.sh +4 -0
- package/scripts/promotion-provider-smoke.mjs +34 -0
- package/scripts/run-setup.sh +30 -0
- package/scripts/setup.mjs +94 -0
- package/scripts/setup.sh +5 -0
- package/scripts/spin-up.sh +45 -0
- package/scripts/wizard.sh +5 -0
- package/server.json +34 -0
- package/skills/openmem/SKILL.md +29 -0
- package/sync/Dockerfile +27 -0
- package/sync/memory_backend.py +87 -0
- package/sync/notebooklm_sync.py +664 -0
- package/wizard/bin/pm.mjs +97 -0
- package/wizard/bin/setup.mjs +33 -0
- package/wizard/lib/compose-version.mjs +32 -0
- package/wizard/lib/compose.mjs +70 -0
- package/wizard/lib/curl.mjs +103 -0
- package/wizard/lib/docker-build.mjs +148 -0
- package/wizard/lib/exec.mjs +41 -0
- package/wizard/lib/indexer.mjs +6 -0
- package/wizard/lib/load-machine-env.mjs +4 -0
- package/wizard/lib/log.mjs +32 -0
- package/wizard/lib/notebooklm-agent-access.mjs +92 -0
- package/wizard/lib/notebooklm.mjs +214 -0
- package/wizard/lib/parse-args.mjs +87 -0
- package/wizard/lib/paths.mjs +39 -0
- package/wizard/lib/pm-config.mjs +163 -0
- package/wizard/lib/ports.mjs +94 -0
- package/wizard/lib/prompts.mjs +57 -0
- package/wizard/lib/provider-wizard/helpers.mjs +98 -0
- package/wizard/lib/provider-wizard/run-steps.mjs +129 -0
- package/wizard/lib/providers/registry.mjs +262 -0
- package/wizard/lib/run-clack.mjs +108 -0
- package/wizard/lib/run-wizard.mjs +755 -0
- package/wizard/lib/validate-setup.mjs +62 -0
- package/wizard/lib/wizard-action.mjs +34 -0
- package/wizard/lib/wizard-conf.mjs +71 -0
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* After the stack is healthy: wait for compose Ollama and pull models the LiteLLM routes need.
|
|
3
|
+
*/
|
|
4
|
+
import { ollamaModelNames } from "./probe.mjs";
|
|
5
|
+
import { ensureState } from "./state.mjs";
|
|
6
|
+
|
|
7
|
+
/** @typedef {import("../../../wizard/lib/provider-wizard/helpers.mjs").ProviderWizardHelpers} Helpers */
|
|
8
|
+
|
|
9
|
+
const HOST_TAGS = "http://127.0.0.1:11434/api/tags";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @param {Record<string, any>} ctx
|
|
13
|
+
* @param {Helpers} h
|
|
14
|
+
*/
|
|
15
|
+
export async function ensureComposeOllamaModels(ctx, h) {
|
|
16
|
+
ensureState(ctx);
|
|
17
|
+
if (ctx.backend !== "ollama" || !ctx.ollamaInCompose) return;
|
|
18
|
+
if (ctx.dry) {
|
|
19
|
+
h.info(`[dry-run] wait for compose Ollama, pull ${ctx.model}`);
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
h.info("waiting for compose Ollama on 127.0.0.1:11434…");
|
|
24
|
+
let body = null;
|
|
25
|
+
for (let i = 0; i < 45; i++) {
|
|
26
|
+
body = h.curlJson(HOST_TAGS);
|
|
27
|
+
if (body) break;
|
|
28
|
+
if (i > 0 && i % 5 === 0) h.info(`… still waiting for Ollama (${i * 2}s)`);
|
|
29
|
+
await new Promise((r) => setTimeout(r, 2000));
|
|
30
|
+
}
|
|
31
|
+
if (!body) {
|
|
32
|
+
h.warn("compose Ollama did not answer on 127.0.0.1:11434; skip model pull");
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
h.ok("compose Ollama is reachable on 127.0.0.1:11434");
|
|
36
|
+
|
|
37
|
+
const present = new Set(ollamaModelNames(body));
|
|
38
|
+
const needed = new Set();
|
|
39
|
+
if (ctx.model) needed.add(ctx.model);
|
|
40
|
+
if (ctx.reflectModel) needed.add(ctx.reflectModel);
|
|
41
|
+
for (const name of needed) {
|
|
42
|
+
if (present.has(name)) {
|
|
43
|
+
h.ok(`Ollama already has ${name}`);
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
const args = h.composeArgs(ctx, "exec", "-T", "ollama", "ollama", "pull", name);
|
|
47
|
+
await h.act(ctx, `pull ${name} into compose Ollama`, "docker", args, {
|
|
48
|
+
cwd: ctx.repoRoot,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/** Hindsight's two host ports: the API and the control plane. */
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { ensureState } from "./state.mjs";
|
|
4
|
+
|
|
5
|
+
/** @typedef {import("../../../wizard/lib/provider-wizard/helpers.mjs").ProviderWizardHelpers} Helpers */
|
|
6
|
+
|
|
7
|
+
const ENV_VARS = ["HINDSIGHT_PORT", "HINDSIGHT_CP_PORT"];
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Pick the API and control-plane ports (env, then the stack .env, then 8888/9999) and
|
|
11
|
+
* export them in process.env for compose.
|
|
12
|
+
* @param {Record<string, any>} ctx
|
|
13
|
+
* @param {Helpers} h
|
|
14
|
+
*/
|
|
15
|
+
export async function resolvePorts(ctx, h) {
|
|
16
|
+
ensureState(ctx);
|
|
17
|
+
const stackEnv = path.join(ctx.repoRoot, ".env");
|
|
18
|
+
const api =
|
|
19
|
+
process.env.HINDSIGHT_PORT || h.envValue(stackEnv, "HINDSIGHT_PORT") || "8888";
|
|
20
|
+
const cp =
|
|
21
|
+
process.env.HINDSIGHT_CP_PORT || h.envValue(stackEnv, "HINDSIGHT_CP_PORT") || "9999";
|
|
22
|
+
if (!/^\d+$/.test(api) || !/^\d+$/.test(cp)) {
|
|
23
|
+
h.die("HINDSIGHT_PORT and HINDSIGHT_CP_PORT must be port numbers");
|
|
24
|
+
}
|
|
25
|
+
ctx.portApi = String(await h.choosePort(ctx, "Hindsight API", Number(api), { envVars: ENV_VARS }));
|
|
26
|
+
ctx.portCp = String(await h.choosePort(ctx, "control plane", Number(cp), { envVars: ENV_VARS }));
|
|
27
|
+
process.env.HINDSIGHT_PORT = ctx.portApi;
|
|
28
|
+
process.env.HINDSIGHT_CP_PORT = ctx.portCp;
|
|
29
|
+
ctx.hindsightUrl = `http://127.0.0.1:${ctx.portApi}`;
|
|
30
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
const TIMEOUT_MS = 8000;
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @param {string} url
|
|
5
|
+
* @returns {Promise<object|null>}
|
|
6
|
+
*/
|
|
7
|
+
export async function fetchJson(url) {
|
|
8
|
+
try {
|
|
9
|
+
const res = await fetch(url, { signal: AbortSignal.timeout(TIMEOUT_MS) });
|
|
10
|
+
if (!res.ok) return null;
|
|
11
|
+
return await res.json();
|
|
12
|
+
} catch {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** @param {object|null} body OpenAI /v1/models or similar */
|
|
18
|
+
export function openAiModelIds(body) {
|
|
19
|
+
if (!body || !Array.isArray(body.data)) return [];
|
|
20
|
+
return body.data.map((m) => m.id).filter(Boolean);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** @param {object|null} body Ollama /api/tags */
|
|
24
|
+
export function ollamaModelNames(body) {
|
|
25
|
+
if (!body || !Array.isArray(body.models)) return [];
|
|
26
|
+
return body.models.map((m) => m.name).filter(Boolean);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function normalizeOpenAiBase(base) {
|
|
30
|
+
let b = base.trim().replace(/\/+$/, "");
|
|
31
|
+
if (!b.endsWith("/v1")) b += "/v1";
|
|
32
|
+
return b;
|
|
33
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hindsight's contribution to scripts/setup.mjs: the LiteLLM proxy secrets file, the
|
|
3
|
+
* LITELLM_CONFIG and port entries of the stack .env, and the hindsight.env migration.
|
|
4
|
+
* Runs as a plain Node script step (no wizard context), so it only reads process
|
|
5
|
+
* environment and the paths it is given.
|
|
6
|
+
*/
|
|
7
|
+
import crypto from "node:crypto";
|
|
8
|
+
import fs from "node:fs";
|
|
9
|
+
import path from "node:path";
|
|
10
|
+
import { envValue } from "../../../wizard/lib/paths.mjs";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @param {{ repo: string, home: string, env: NodeJS.ProcessEnv }} opts
|
|
14
|
+
* @returns {{ entries: [string, string][], drop: string[], report: string[] }}
|
|
15
|
+
* entries: KEY=value lines to add to the stack .env; drop: keys to remove first;
|
|
16
|
+
* report: KEY=value strings echoed in the "wrote .env" line
|
|
17
|
+
*/
|
|
18
|
+
export function prepareStackEnv({ repo, home, env }) {
|
|
19
|
+
const litellmEnv =
|
|
20
|
+
env.LITELLM_ENV_FILE || path.join(home, ".config/hindsight/litellm.env");
|
|
21
|
+
const overrideConfig = path.join(home, ".config/hindsight/litellm.yaml");
|
|
22
|
+
const backend = env.MEMORY_BACKEND || "lemond";
|
|
23
|
+
|
|
24
|
+
if (!["lemond", "ollama"].includes(backend)) {
|
|
25
|
+
throw new Error(`MEMORY_BACKEND must be lemond or ollama (got: ${backend})`);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (!fs.existsSync(litellmEnv)) {
|
|
29
|
+
const key = crypto.randomBytes(24).toString("hex");
|
|
30
|
+
const body =
|
|
31
|
+
"# LiteLLM proxy secrets. Read by the litellm compose service. Do not commit.\n" +
|
|
32
|
+
`LITELLM_MASTER_KEY=sk-${key}\n` +
|
|
33
|
+
"# lemond ignores the key; any non-empty value works.\n" +
|
|
34
|
+
"LEMOND_API_KEY=placeholder\n" +
|
|
35
|
+
"# Hosted models: add the provider key here, e.g. ANTHROPIC_API_KEY=..., and reference it as os.environ/ANTHROPIC_API_KEY.\n";
|
|
36
|
+
fs.writeFileSync(litellmEnv, body, { mode: 0o600 });
|
|
37
|
+
console.log(`created ${litellmEnv} (mode 600)`);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const litellmConfig = fs.existsSync(overrideConfig)
|
|
41
|
+
? overrideConfig
|
|
42
|
+
: path.join(repo, "config", `litellm.${backend}.yaml`);
|
|
43
|
+
|
|
44
|
+
const profiles = ["hindsight"];
|
|
45
|
+
if (env.MEMORY_OLLAMA_IN_COMPOSE === "1") profiles.push("ollama");
|
|
46
|
+
const notebookYml = path.join(home, ".config/hindsight/notebooklm.yml");
|
|
47
|
+
if (
|
|
48
|
+
fs.existsSync(notebookYml) &&
|
|
49
|
+
/^enabled:\s*true\b/m.test(fs.readFileSync(notebookYml, "utf8"))
|
|
50
|
+
) {
|
|
51
|
+
profiles.push("notebooklm");
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** @type {[string, string][]} */
|
|
55
|
+
const entries = [
|
|
56
|
+
["LITELLM_CONFIG", litellmConfig],
|
|
57
|
+
["COMPOSE_PROFILES", profiles.join(",")],
|
|
58
|
+
];
|
|
59
|
+
const drop = ["LITELLM_CONFIG", "COMPOSE_PROFILES"];
|
|
60
|
+
if (env.HINDSIGHT_PORT) {
|
|
61
|
+
drop.push("HINDSIGHT_PORT");
|
|
62
|
+
entries.push(["HINDSIGHT_PORT", env.HINDSIGHT_PORT]);
|
|
63
|
+
}
|
|
64
|
+
if (env.HINDSIGHT_CP_PORT) {
|
|
65
|
+
drop.push("HINDSIGHT_CP_PORT");
|
|
66
|
+
entries.push(["HINDSIGHT_CP_PORT", env.HINDSIGHT_CP_PORT]);
|
|
67
|
+
}
|
|
68
|
+
return { entries, drop, report: [`LITELLM_CONFIG=${litellmConfig}`] };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Point hindsight.env at the LiteLLM proxy (backs the file up first; prints no values).
|
|
73
|
+
* @param {{ repo: string, home: string, env: NodeJS.ProcessEnv }} opts
|
|
74
|
+
*/
|
|
75
|
+
export function migrateEnv({ home, env }) {
|
|
76
|
+
const hindsightEnv =
|
|
77
|
+
env.HINDSIGHT_ENV_FILE || path.join(home, ".config/hindsight/hindsight.env");
|
|
78
|
+
const litellmEnv =
|
|
79
|
+
env.LITELLM_ENV_FILE || path.join(home, ".config/hindsight/litellm.env");
|
|
80
|
+
if (!fs.existsSync(hindsightEnv)) {
|
|
81
|
+
throw new Error(`no ${hindsightEnv} to migrate`);
|
|
82
|
+
}
|
|
83
|
+
const key = envValue(litellmEnv, "LITELLM_MASTER_KEY");
|
|
84
|
+
if (!key) {
|
|
85
|
+
throw new Error(`LITELLM_MASTER_KEY missing from ${litellmEnv}`);
|
|
86
|
+
}
|
|
87
|
+
const backup = `${hindsightEnv}.bak-${Date.now()}`;
|
|
88
|
+
fs.copyFileSync(hindsightEnv, backup);
|
|
89
|
+
const llmLines = [
|
|
90
|
+
"HINDSIGHT_API_LLM_PROVIDER=openai",
|
|
91
|
+
"HINDSIGHT_API_LLM_BASE_URL=http://litellm:4000/v1",
|
|
92
|
+
"HINDSIGHT_API_LLM_MODEL=memory-reflect",
|
|
93
|
+
`HINDSIGHT_API_LLM_API_KEY=${key}`,
|
|
94
|
+
"HINDSIGHT_API_RETAIN_LLM_MODEL=memory-retain",
|
|
95
|
+
"HINDSIGHT_API_REFLECT_LLM_MODEL=memory-reflect",
|
|
96
|
+
"HINDSIGHT_API_CONSOLIDATION_LLM_MODEL=memory-consolidate",
|
|
97
|
+
"HINDSIGHT_API_MENTAL_MODEL_REFRESH_LLM_MODEL=memory-reflect",
|
|
98
|
+
];
|
|
99
|
+
const keys = llmLines.map((l) => l.split("=")[0]);
|
|
100
|
+
const keyRe = new RegExp(`^(${keys.join("|")})=`);
|
|
101
|
+
const rest = fs
|
|
102
|
+
.readFileSync(hindsightEnv, "utf8")
|
|
103
|
+
.split("\n")
|
|
104
|
+
.filter((l) => l && !keyRe.test(l));
|
|
105
|
+
fs.writeFileSync(hindsightEnv, `${[...rest, ...llmLines].join("\n")}\n`, { mode: 0o600 });
|
|
106
|
+
console.log(
|
|
107
|
+
`migrated ${hindsightEnv} to the LiteLLM proxy (backup: ${backup}). Values not printed.`,
|
|
108
|
+
);
|
|
109
|
+
console.log("Next: docker compose up -d (recreates hindsight with the new env)");
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Printed after the .env is written when hindsight.env still calls a model server directly.
|
|
114
|
+
* @param {{ home: string, env: NodeJS.ProcessEnv }} opts
|
|
115
|
+
* @returns {string[]}
|
|
116
|
+
*/
|
|
117
|
+
export function setupNotices({ home, env }) {
|
|
118
|
+
const hindsightEnv =
|
|
119
|
+
env.HINDSIGHT_ENV_FILE || path.join(home, ".config/hindsight/hindsight.env");
|
|
120
|
+
if (
|
|
121
|
+
fs.existsSync(hindsightEnv) &&
|
|
122
|
+
/^HINDSIGHT_API_LLM_BASE_URL=http:\/\/host\.docker\.internal:/m.test(
|
|
123
|
+
fs.readFileSync(hindsightEnv, "utf8"),
|
|
124
|
+
)
|
|
125
|
+
) {
|
|
126
|
+
return [
|
|
127
|
+
`NOTE: ${hindsightEnv} still calls the model server directly.`,
|
|
128
|
+
" Run: node scripts/setup.mjs --migrate-env (backs up the file, rewrites only LLM lines)",
|
|
129
|
+
];
|
|
130
|
+
}
|
|
131
|
+
return [];
|
|
132
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
|
|
3
|
+
/** Models the shipped LiteLLM routes (config/litellm.<backend>.yaml) use for retain/consolidate. */
|
|
4
|
+
export const LEMOND_DEFAULT_MODEL = "Gemma-4-E2B-it-GGUF";
|
|
5
|
+
export const OLLAMA_DEFAULT_MODEL = "gemma4:e2b";
|
|
6
|
+
|
|
7
|
+
/** LiteLLM api_base when Ollama runs on the host (default). */
|
|
8
|
+
export const OLLAMA_HOST_API_BASE = "http://host.docker.internal:11434";
|
|
9
|
+
/** LiteLLM api_base when Ollama is the compose `ollama` service. */
|
|
10
|
+
export const OLLAMA_COMPOSE_API_BASE = "http://ollama:11434";
|
|
11
|
+
|
|
12
|
+
export const BACKENDS = ["lemond", "ollama", "custom"];
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Fill the Hindsight fields of the wizard context. Idempotent: a field that is already
|
|
16
|
+
* set (a test) is kept.
|
|
17
|
+
* @param {Record<string, any>} ctx
|
|
18
|
+
*/
|
|
19
|
+
export function ensureState(ctx) {
|
|
20
|
+
const hs = ctx.hsDir || path.join(ctx.home || "", ".config/hindsight");
|
|
21
|
+
const defaults = {
|
|
22
|
+
envFile: path.join(hs, "hindsight.env"),
|
|
23
|
+
litellmEnv: path.join(hs, "litellm.env"),
|
|
24
|
+
litellmOverride: path.join(hs, "litellm.yaml"),
|
|
25
|
+
agentJson: path.join(ctx.home || "", ".hindsight/coding-agent.json"),
|
|
26
|
+
lemondBase: "http://127.0.0.1:13305",
|
|
27
|
+
ollamaBase: "http://127.0.0.1:11434",
|
|
28
|
+
lemondDefaultModel: LEMOND_DEFAULT_MODEL,
|
|
29
|
+
ollamaDefaultModel: OLLAMA_DEFAULT_MODEL,
|
|
30
|
+
customDefaultBase: "http://host.docker.internal:8080/v1",
|
|
31
|
+
backend: "",
|
|
32
|
+
model: "",
|
|
33
|
+
reflectModel: "",
|
|
34
|
+
apiBase: "",
|
|
35
|
+
lemondJson: null,
|
|
36
|
+
lemondNames: [],
|
|
37
|
+
ollamaJson: null,
|
|
38
|
+
ollamaNames: [],
|
|
39
|
+
/** True when Ollama runs as the compose profile `ollama` (no host install). */
|
|
40
|
+
ollamaInCompose: false,
|
|
41
|
+
portApi: "",
|
|
42
|
+
portCp: "",
|
|
43
|
+
hindsightUrl: "",
|
|
44
|
+
};
|
|
45
|
+
for (const [key, value] of Object.entries(defaults)) {
|
|
46
|
+
if (ctx[key] === undefined) ctx[key] = value;
|
|
47
|
+
}
|
|
48
|
+
return ctx;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* @param {Record<string, any>} ctx
|
|
53
|
+
* @param {import("../../../wizard/lib/provider-wizard/helpers.mjs").ProviderWizardHelpers} h
|
|
54
|
+
* @param {string} key wizard.conf key
|
|
55
|
+
*/
|
|
56
|
+
export function confGet(ctx, h, key) {
|
|
57
|
+
return h.envValue(ctx.wizardConf, key);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* The stored value for `key`, but only when wizard.conf was written for the same backend.
|
|
62
|
+
*/
|
|
63
|
+
export function storedOr(ctx, h, backend, key, fallback) {
|
|
64
|
+
if (confGet(ctx, h, "MEMORY_BACKEND") !== backend) return fallback;
|
|
65
|
+
return confGet(ctx, h, key) || fallback;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** PM_BACKEND, then the saved MEMORY_BACKEND, then lemond. */
|
|
69
|
+
export function backendDefault(ctx, h) {
|
|
70
|
+
const pm = process.env.PM_BACKEND;
|
|
71
|
+
if (BACKENDS.includes(pm)) return pm;
|
|
72
|
+
const saved = confGet(ctx, h, "MEMORY_BACKEND");
|
|
73
|
+
if (BACKENDS.includes(saved)) return saved;
|
|
74
|
+
return "lemond";
|
|
75
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/** Hindsight's contribution to the wizard's closing summary. */
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import { composeCommandText } from "../../../wizard/lib/compose.mjs";
|
|
4
|
+
import { ensureState } from "./state.mjs";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Lines the meta wizard prints in its summary, in the order it prints them.
|
|
8
|
+
* @param {Record<string, any>} ctx
|
|
9
|
+
* @returns {{ running: string[], configFiles: string[], notes: string[], logServices: string[] }}
|
|
10
|
+
*/
|
|
11
|
+
export function summary(ctx) {
|
|
12
|
+
ensureState(ctx);
|
|
13
|
+
const modelWhere =
|
|
14
|
+
ctx.backend === "ollama" && ctx.ollamaInCompose
|
|
15
|
+
? "compose Ollama (profile ollama)"
|
|
16
|
+
: "the LiteLLM proxy (internal network)";
|
|
17
|
+
const running = [
|
|
18
|
+
` Hindsight API ${ctx.hindsightUrl} (docs: ${ctx.hindsightUrl}/docs)`,
|
|
19
|
+
` control plane http://127.0.0.1:${ctx.portCp}`,
|
|
20
|
+
` model ${ctx.backend}, ${ctx.model} via ${modelWhere}`,
|
|
21
|
+
];
|
|
22
|
+
if (ctx.ollamaInCompose) {
|
|
23
|
+
running.push(` Ollama http://127.0.0.1:11434 (compose service; LiteLLM uses http://ollama:11434)`);
|
|
24
|
+
}
|
|
25
|
+
const configFiles = [` ${ctx.envFile}`];
|
|
26
|
+
if (fs.existsSync(ctx.litellmOverride)) {
|
|
27
|
+
configFiles.push(` ${ctx.litellmOverride} (model routes; replaces config/litellm.*.yaml)`);
|
|
28
|
+
} else if (ctx.backend === "custom") {
|
|
29
|
+
configFiles.push(` ${ctx.litellmOverride} (missing: custom backend needs it)`);
|
|
30
|
+
} else configFiles.push(` config/litellm.${ctx.backend}.yaml (shipped routes)`);
|
|
31
|
+
const litellmUp = composeCommandText(ctx, "up", "-d", "litellm");
|
|
32
|
+
const notes = [
|
|
33
|
+
`Control plane (banks, ops, LLM queue): http://127.0.0.1:${ctx.portCp}`,
|
|
34
|
+
`Reflect needs a capable model (memory-reflect alias). On a weak or remote LLM, edit ${ctx.litellmOverride} and point memory-reflect at Meridian, an OpenAI-compatible host, or a stronger local model — then: ${litellmUp}`,
|
|
35
|
+
`Change later: rerun ./setup. For a route-only edit, run: ${litellmUp}`,
|
|
36
|
+
];
|
|
37
|
+
if (ctx.smokeFailed) {
|
|
38
|
+
notes.unshift(
|
|
39
|
+
`Smoke retain/recall did not finish — stack is up; fix the model/LLM queue via http://127.0.0.1:${ctx.portCp} then re-run setup or: curl the wizard-smoke retain against ${ctx.hindsightUrl}`,
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
return { running, configFiles, notes, logServices: ["hindsight", "litellm"] };
|
|
43
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hindsight checks that run after the meta wizard's provider-neutral validation:
|
|
3
|
+
* wizard.conf backend keys, hindsight.env, litellm.env, the LiteLLM wiring, the stack
|
|
4
|
+
* .env's Hindsight keys, and a model override for a non-shipped model.
|
|
5
|
+
*/
|
|
6
|
+
import fs from "node:fs";
|
|
7
|
+
import path from "node:path";
|
|
8
|
+
import { ensureState } from "./state.mjs";
|
|
9
|
+
|
|
10
|
+
/** @typedef {import("../../../wizard/lib/provider-wizard/helpers.mjs").ProviderWizardHelpers} Helpers */
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Appends to ctx.validationErrors and ctx.validationChecks.
|
|
14
|
+
* @param {Record<string, any>} ctx
|
|
15
|
+
* @param {Helpers} h
|
|
16
|
+
*/
|
|
17
|
+
export function validateHindsight(ctx, h) {
|
|
18
|
+
ensureState(ctx);
|
|
19
|
+
const errors = (ctx.validationErrors ||= []);
|
|
20
|
+
const checks = (ctx.validationChecks ||= []);
|
|
21
|
+
const conf = (key) => h.envValue(ctx.wizardConf, key);
|
|
22
|
+
|
|
23
|
+
if (fs.existsSync(ctx.wizardConf)) {
|
|
24
|
+
for (const key of ["MEMORY_BACKEND", "MEMORY_MODEL"]) {
|
|
25
|
+
if (!conf(key)) errors.push(`wizard.conf missing ${key}`);
|
|
26
|
+
else checks.push(`wizard.conf ${key} set`);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (!fs.existsSync(ctx.envFile)) errors.push(`missing ${ctx.envFile}`);
|
|
31
|
+
else {
|
|
32
|
+
if (fs.statSync(ctx.envFile).mode & 0o077) {
|
|
33
|
+
errors.push(`${ctx.envFile} must not be world/group readable (want mode 600)`);
|
|
34
|
+
}
|
|
35
|
+
checks.push("hindsight.env present");
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (!fs.existsSync(ctx.litellmEnv)) errors.push(`missing ${ctx.litellmEnv}`);
|
|
39
|
+
else {
|
|
40
|
+
if (fs.statSync(ctx.litellmEnv).mode & 0o077) {
|
|
41
|
+
errors.push(`${ctx.litellmEnv} must not be world/group readable (want mode 600)`);
|
|
42
|
+
}
|
|
43
|
+
if (!h.envValue(ctx.litellmEnv, "LITELLM_MASTER_KEY")) {
|
|
44
|
+
errors.push("litellm.env missing LITELLM_MASTER_KEY");
|
|
45
|
+
}
|
|
46
|
+
checks.push("litellm.env present");
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const stackEnv = path.join(ctx.repoRoot, ".env");
|
|
50
|
+
if (fs.existsSync(stackEnv)) {
|
|
51
|
+
for (const key of ["LITELLM_CONFIG", "HINDSIGHT_PORT"]) {
|
|
52
|
+
if (!h.envValue(stackEnv, key)) errors.push(`repo .env missing ${key}`);
|
|
53
|
+
}
|
|
54
|
+
checks.push("repo .env ports and litellm config");
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const llmBase = h.envValue(ctx.envFile, "HINDSIGHT_API_LLM_BASE_URL");
|
|
58
|
+
const llmKey = h.envValue(ctx.envFile, "HINDSIGHT_API_LLM_API_KEY");
|
|
59
|
+
const master = h.envValue(ctx.litellmEnv, "LITELLM_MASTER_KEY");
|
|
60
|
+
if (llmBase !== "http://litellm:4000/v1") {
|
|
61
|
+
errors.push("hindsight.env not wired to LiteLLM proxy (HINDSIGHT_API_LLM_BASE_URL)");
|
|
62
|
+
}
|
|
63
|
+
if (!llmKey || !master || llmKey !== master) {
|
|
64
|
+
errors.push("hindsight.env LLM API key must match LITELLM_MASTER_KEY");
|
|
65
|
+
}
|
|
66
|
+
checks.push("LiteLLM proxy wiring");
|
|
67
|
+
|
|
68
|
+
const backend = conf("MEMORY_BACKEND");
|
|
69
|
+
const model = conf("MEMORY_MODEL");
|
|
70
|
+
const shipped = backend === "ollama" ? ctx.ollamaDefaultModel : ctx.lemondDefaultModel;
|
|
71
|
+
const reflect = conf("MEMORY_REFLECT_MODEL");
|
|
72
|
+
if (backend !== "custom" && model !== shipped && !reflect && !fs.existsSync(ctx.litellmOverride)) {
|
|
73
|
+
errors.push("custom model without litellm.yaml override");
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "hindsight",
|
|
3
|
+
"label": "Hindsight",
|
|
4
|
+
"status": "supported",
|
|
5
|
+
"compose": {
|
|
6
|
+
"services": ["litellm", "hindsight", "indexer"],
|
|
7
|
+
"healthService": "hindsight",
|
|
8
|
+
"healthPath": "/health",
|
|
9
|
+
"hostPortEnv": "HINDSIGHT_PORT",
|
|
10
|
+
"defaultHostPort": 8888,
|
|
11
|
+
"logServices": ["hindsight", "litellm"],
|
|
12
|
+
"profile": "hindsight",
|
|
13
|
+
"files": []
|
|
14
|
+
},
|
|
15
|
+
"indexer": {
|
|
16
|
+
"apiUrlEnv": "HINDSIGHT_URL",
|
|
17
|
+
"sink": "hindsight",
|
|
18
|
+
"sinkDriver": "hindsight-rest-v1"
|
|
19
|
+
},
|
|
20
|
+
"codingAgent": {
|
|
21
|
+
"pluginPackage": "@vectorize-io/hindsight-coding-agents",
|
|
22
|
+
"configTemplate": "config/coding-agent.json"
|
|
23
|
+
},
|
|
24
|
+
"settings": {
|
|
25
|
+
"nativeDocs": "https://github.com/vectorize-io/hindsight",
|
|
26
|
+
"nativeConfigPaths": [
|
|
27
|
+
"~/.config/hindsight/hindsight.env",
|
|
28
|
+
"~/.config/hindsight/litellm.yaml",
|
|
29
|
+
"config/litellm.lemond.yaml"
|
|
30
|
+
],
|
|
31
|
+
"wizardOwns": [
|
|
32
|
+
"compose stack and ports",
|
|
33
|
+
"index.yml sources",
|
|
34
|
+
"LiteLLM proxy wiring for Hindsight LLM aliases",
|
|
35
|
+
"coding-agent.json symlink"
|
|
36
|
+
]
|
|
37
|
+
},
|
|
38
|
+
"wizardHooks": {
|
|
39
|
+
"module": "wizard.mjs",
|
|
40
|
+
"delegates": {}
|
|
41
|
+
},
|
|
42
|
+
"wizard": {
|
|
43
|
+
"smokeBank": "wizard-smoke",
|
|
44
|
+
"retainMethod": "POST",
|
|
45
|
+
"retainPath": "/v1/default/banks/{bank}/memories",
|
|
46
|
+
"retainBody": "{\"items\":[{\"content\":\"Wizard smoke test: the project codename is heron.\"}],\"async\":false}",
|
|
47
|
+
"recallMethod": "POST",
|
|
48
|
+
"recallPath": "/v1/default/banks/{bank}/memories/recall",
|
|
49
|
+
"recallBody": "{\"query\":\"What is the project codename?\"}",
|
|
50
|
+
"recallMustMatch": "heron",
|
|
51
|
+
"smokeRetainTimeoutSec": 90,
|
|
52
|
+
"smokeRecallTimeoutSec": 60,
|
|
53
|
+
"retainHint": "Open {controlPlane} (ops queue) and {api}/docs. LLM is shared (one retain at a time): cancel other banks' pending retains, or stop coding-agent/NotebookLM sync while smoking. If Ollama shows size_vram=0 it is on CPU — use GPU/ROCm or a faster model in ~/.config/hindsight/litellm.yaml, then: {compose} up -d litellm. Logs: {logs}",
|
|
54
|
+
"recallHint": "Open {controlPlane}; check memory-retain/reflect routes ({compose} logs litellm). Logs: {logs}",
|
|
55
|
+
"deleteMethod": "DELETE",
|
|
56
|
+
"deletePath": "/v1/default/banks/{bank}"
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hindsight sub-wizard (hook API v2). Owns everything Hindsight-specific that the meta
|
|
3
|
+
* wizard used to do inline: the lemond / Ollama / custom model flow and LiteLLM routes,
|
|
4
|
+
* hindsight.env and litellm.env, the HINDSIGHT_PORT / HINDSIGHT_CP_PORT pair, Hindsight
|
|
5
|
+
* validation, and the summary lines. See lib/ for the implementations and
|
|
6
|
+
* docs/provider-wizard-hooks.md for the hook contract.
|
|
7
|
+
*
|
|
8
|
+
* This module must not import wizard/lib/run-wizard.mjs; it receives `helpers`.
|
|
9
|
+
*/
|
|
10
|
+
import { applyConfigEnv, configToml as buildConfigToml } from "./lib/config.mjs";
|
|
11
|
+
import { setupCodingAgent as linkCodingAgent } from "./lib/coding-agent.mjs";
|
|
12
|
+
import { clackSetup as askInClack } from "./lib/clack.mjs";
|
|
13
|
+
import { setupEnv as setupHindsightEnv, stackEnv as hindsightStackEnv } from "./lib/env-setup.mjs";
|
|
14
|
+
import { chooseModelBackend } from "./lib/model-backend.mjs";
|
|
15
|
+
import { ensureComposeOllamaModels } from "./lib/ollama-compose.mjs";
|
|
16
|
+
import { resolvePorts as resolveHindsightPorts } from "./lib/ports.mjs";
|
|
17
|
+
import { backendDefault, confGet, ensureState } from "./lib/state.mjs";
|
|
18
|
+
import { summary as hindsightSummary } from "./lib/summary.mjs";
|
|
19
|
+
import { validateHindsight } from "./lib/validate.mjs";
|
|
20
|
+
|
|
21
|
+
// Called by scripts/setup.mjs (the stack .env writer), not by the wizard.
|
|
22
|
+
export { migrateEnv, prepareStackEnv, setupNotices } from "./lib/stack-env.mjs";
|
|
23
|
+
|
|
24
|
+
/** @typedef {import('../../wizard/lib/provider-wizard/helpers.mjs').ProviderWizardHelpers} Helpers */
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Quiet: fill Hindsight's context fields, map [stack]/[model] TOML defaults into the
|
|
28
|
+
* environment (including LEMOND_BASE from [model].lemond.host).
|
|
29
|
+
* @param {Record<string, any>} ctx
|
|
30
|
+
* @param {Helpers} h
|
|
31
|
+
*/
|
|
32
|
+
export async function init(ctx, h) {
|
|
33
|
+
ensureState(ctx);
|
|
34
|
+
applyConfigEnv(ctx.pmMerged || {});
|
|
35
|
+
if (process.env.LEMOND_BASE) ctx.lemondBase = process.env.LEMOND_BASE;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* @param {Record<string, any>} ctx
|
|
40
|
+
* @param {Helpers} h
|
|
41
|
+
*/
|
|
42
|
+
export async function afterProvider(ctx, h) {
|
|
43
|
+
h.info("Hindsight uses LiteLLM aliases (memory-retain / reflect / consolidate).");
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Step "3. Model backend": lemond, Ollama or a custom OpenAI-compatible URL behind the
|
|
48
|
+
* LiteLLM proxy.
|
|
49
|
+
* @param {Record<string, any>} ctx
|
|
50
|
+
* @param {Helpers} h
|
|
51
|
+
*/
|
|
52
|
+
export async function configureModelRouting(ctx, h) {
|
|
53
|
+
await chooseModelBackend(ctx, h);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Hindsight's API and control-plane ports. */
|
|
57
|
+
export const resolvePorts = resolveHindsightPorts;
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Quiet: description and environment for the stack setup script (scripts/setup.mjs).
|
|
61
|
+
* @param {Record<string, any>} ctx
|
|
62
|
+
*/
|
|
63
|
+
export function stackEnv(ctx) {
|
|
64
|
+
return hindsightStackEnv(ctx);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** hindsight.env, litellm.env wiring. */
|
|
68
|
+
export const setupEnv = setupHindsightEnv;
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Quiet: reload the model choice from wizard.conf for `--up`.
|
|
72
|
+
* @param {Record<string, any>} ctx
|
|
73
|
+
* @param {Helpers} h
|
|
74
|
+
*/
|
|
75
|
+
export async function hydrate(ctx, h) {
|
|
76
|
+
ensureState(ctx);
|
|
77
|
+
ctx.backend = confGet(ctx, h, "MEMORY_BACKEND") || backendDefault(ctx, h);
|
|
78
|
+
ctx.model = confGet(ctx, h, "MEMORY_MODEL") || ctx.lemondDefaultModel;
|
|
79
|
+
ctx.apiBase = confGet(ctx, h, "MEMORY_API_BASE") || "";
|
|
80
|
+
ctx.reflectModel = confGet(ctx, h, "MEMORY_REFLECT_MODEL") || "";
|
|
81
|
+
ctx.ollamaInCompose = confGet(ctx, h, "MEMORY_OLLAMA_IN_COMPOSE") === "1";
|
|
82
|
+
applyComposeProfiles(ctx);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Ensure compose profile `ollama` is on the up command when MEMORY_OLLAMA_IN_COMPOSE=1.
|
|
87
|
+
* @param {Record<string, any>} ctx
|
|
88
|
+
* @param {Helpers} _h
|
|
89
|
+
*/
|
|
90
|
+
export async function beforeStack(ctx, _h) {
|
|
91
|
+
ensureState(ctx);
|
|
92
|
+
applyComposeProfiles(ctx);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Pull models into compose Ollama after the stack is up (host has no ollama CLI).
|
|
97
|
+
* @param {Record<string, any>} ctx
|
|
98
|
+
* @param {Helpers} h
|
|
99
|
+
*/
|
|
100
|
+
export async function afterStackHealthy(ctx, h) {
|
|
101
|
+
await ensureComposeOllamaModels(ctx, h);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** @param {Record<string, any>} ctx */
|
|
105
|
+
function applyComposeProfiles(ctx) {
|
|
106
|
+
const extra = [...(ctx.extraComposeProfiles || [])];
|
|
107
|
+
if (ctx.ollamaInCompose && !extra.includes("ollama")) extra.push("ollama");
|
|
108
|
+
ctx.extraComposeProfiles = extra;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Quiet: tables merged into config.toml.
|
|
113
|
+
* @param {Record<string, any>} ctx
|
|
114
|
+
*/
|
|
115
|
+
export function configToml(ctx) {
|
|
116
|
+
ensureState(ctx);
|
|
117
|
+
return buildConfigToml(ctx);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Quiet: lines for the closing summary.
|
|
122
|
+
* @param {Record<string, any>} ctx
|
|
123
|
+
*/
|
|
124
|
+
export function summary(ctx) {
|
|
125
|
+
return hindsightSummary(ctx);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Appends to ctx.validationErrors / ctx.validationChecks after the shared checks.
|
|
130
|
+
* @param {Record<string, any>} ctx
|
|
131
|
+
* @param {Helpers} h
|
|
132
|
+
*/
|
|
133
|
+
export async function extendValidation(ctx, h) {
|
|
134
|
+
validateHindsight(ctx, h);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/** Link ~/.hindsight/coding-agent.json and check optInPaths. */
|
|
138
|
+
export const setupCodingAgent = linkCodingAgent;
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Clack-only: ask the model questions, return the environment that makes the wizard
|
|
142
|
+
* skip its own (see WIZARD_SKIP_BACKEND_STEP).
|
|
143
|
+
* @param {Record<string, any>} ctx
|
|
144
|
+
* @param {typeof import("@clack/prompts")} ui
|
|
145
|
+
* @param {Helpers} h
|
|
146
|
+
*/
|
|
147
|
+
export async function clackSetup(ctx, ui, h) {
|
|
148
|
+
return askInClack(ctx, ui, h);
|
|
149
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "letta",
|
|
3
|
+
"label": "Letta",
|
|
4
|
+
"status": "planned",
|
|
5
|
+
"notes": "Placeholder manifest. Letta (formerly MemGPT) server integration is not wired; track via issues before changing status.",
|
|
6
|
+
"settings": {
|
|
7
|
+
"nativeDocs": "https://docs.letta.com/",
|
|
8
|
+
"nativeConfigPaths": [],
|
|
9
|
+
"wizardOwns": []
|
|
10
|
+
},
|
|
11
|
+
"compose": {
|
|
12
|
+
"profile": "letta",
|
|
13
|
+
"files": [],
|
|
14
|
+
"services": [],
|
|
15
|
+
"healthService": null,
|
|
16
|
+
"healthPath": null,
|
|
17
|
+
"hostPortEnv": null,
|
|
18
|
+
"defaultHostPort": null
|
|
19
|
+
},
|
|
20
|
+
"indexer": {
|
|
21
|
+
"apiUrlEnv": "LETTA_API_URL",
|
|
22
|
+
"sink": "letta"
|
|
23
|
+
},
|
|
24
|
+
"codingAgent": {
|
|
25
|
+
"pluginPackage": null,
|
|
26
|
+
"configTemplate": null
|
|
27
|
+
},
|
|
28
|
+
"wizard": {}
|
|
29
|
+
}
|