@bcmyguest/openmem 0.6.3 → 0.9.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.
- package/{skills → .agents/skills}/openmem/SKILL.md +5 -0
- package/README.md +81 -76
- package/cli/coding-agent/capabilities.mjs +14 -3
- package/cli/coding-agent/context-store.mjs +81 -0
- package/cli/coding-agent/context-trim.mjs +899 -0
- package/cli/coding-agent/file-io.mjs +72 -0
- package/cli/coding-agent/hook-harnesses.mjs +2 -1
- package/cli/coding-agent/hook-runner.mjs +28 -4
- package/cli/coding-agent/install-access.mjs +43 -17
- package/cli/coding-agent/install-context-proxy.mjs +333 -0
- package/cli/coding-agent/install-rtk.mjs +60 -0
- package/cli/coding-agent/install-shim.mjs +119 -15
- package/cli/coding-agent/opencode-config.mjs +21 -5
- package/cli/coding-agent/opencode-plugin.mjs +39 -3
- package/cli/coding-agent/opencode2-plugin/index.js +299 -0
- package/cli/coding-agent/recall-frame.mjs +29 -0
- package/cli/coding-agent/remove-upstream-access.mjs +122 -0
- package/cli/coding-agent/runtime.mjs +2 -1
- package/cli/coding-agent/session-db.mjs +626 -0
- package/cli/coding-agent/session-facade.mjs +30 -6
- package/cli/coding-agent/toml-edit.mjs +161 -0
- package/cli/context-management.mjs +254 -0
- package/cli/feed-sync.mjs +18 -2
- package/cli/install-coding-agent.mjs +173 -40
- package/cli/mcp/context-sql-runner.mjs +58 -0
- package/cli/mcp/context-store.mjs +670 -0
- package/cli/mcp/knowledge.mjs +333 -0
- package/cli/mcp/memory-facade.mjs +99 -33
- package/cli/openmem-mcp.mjs +3 -0
- package/cli/openmem.mjs +18 -1
- package/cli/schedule.mjs +176 -0
- package/cli/stack.mjs +2 -0
- package/cli/switch-memory-provider.mjs +1 -1
- package/compose.yaml +67 -4
- package/config/hindsight.env.example +2 -0
- package/config/openmem.toml.example +64 -2
- package/docs/architecture.md +35 -9
- package/docs/coding-agent-hooks.md +26 -2
- package/docs/coding-agents.md +27 -7
- package/docs/compatibility.md +80 -0
- package/docs/concepts.md +30 -7
- package/docs/context-management.md +230 -0
- package/docs/context-proxy.md +115 -0
- package/docs/context-research.md +226 -0
- package/docs/context-store.md +277 -0
- package/docs/evidence-plan.md +216 -0
- package/docs/hindsight.md +5 -2
- package/docs/mcp-setup.md +5 -2
- package/docs/mem0.md +39 -1
- package/docs/memory-providers.md +2 -2
- package/docs/meridian-litellm.md +198 -0
- package/docs/model-configuration.md +22 -0
- package/docs/npx-install.md +6 -4
- package/docs/openmem-config.md +2 -2
- package/docs/quickstart.md +12 -6
- package/docs/setup-wizard.md +5 -4
- package/docs/sources-feeds.md +9 -22
- package/package.json +5 -4
- package/providers/cognee/OPERATOR.md +2 -2
- package/providers/hindsight/lib/clack.mjs +122 -20
- package/providers/hindsight/lib/coding-agent.mjs +1 -1
- package/providers/hindsight/lib/config.mjs +6 -0
- package/providers/hindsight/lib/env-setup.mjs +2 -1
- package/providers/hindsight/lib/litellm-routes.mjs +21 -12
- package/providers/hindsight/lib/meridian.mjs +307 -0
- package/providers/hindsight/lib/model-backend.mjs +38 -9
- package/providers/hindsight/lib/probe.mjs +21 -0
- package/providers/hindsight/lib/stack-env.mjs +5 -12
- package/providers/hindsight/lib/state.mjs +6 -1
- package/providers/hindsight/lib/summary.mjs +16 -2
- package/providers/hindsight/lib/validate.mjs +6 -0
- package/providers/hindsight/wizard.mjs +3 -2
- package/providers/mem0/OPERATOR.md +21 -2
- package/providers/mem0/compose.yaml +24 -2
- package/providers/mem0/lib/coding-agent.mjs +1 -1
- package/providers/mem0/lib/configure.mjs +66 -0
- package/providers/mem0/lib/litellm.mjs +457 -0
- package/providers/mem0/lib/stack-env.mjs +40 -1
- package/providers/mem0/lib/summary.mjs +30 -2
- package/providers/mem0/manifest.json +1 -1
- package/providers/mem0/wizard.mjs +15 -2
- package/proxy/config.yaml +33 -0
- package/proxy/openmem_context/__init__.py +6 -0
- package/proxy/openmem_context/adapters.py +504 -0
- package/proxy/openmem_context/compress.py +53 -0
- package/proxy/openmem_context/core.py +608 -0
- package/proxy/openmem_context/embed.py +212 -0
- package/proxy/openmem_context/recall.py +49 -0
- package/proxy/openmem_context/session.py +70 -0
- package/proxy/openmem_context/sessiondb.py +276 -0
- package/proxy/openmem_context/settings.py +134 -0
- package/proxy/openmem_context/store.py +129 -0
- package/proxy/openmem_context/tokens.py +123 -0
- package/proxy/openmem_context/ttl.py +78 -0
- package/proxy/openmem_context_hook.py +34 -0
- package/scripts/check-pack.mjs +2 -1
- package/scripts/run-setup.sh +14 -5
- package/scripts/setup.mjs +14 -1
- package/server.json +2 -2
- package/wizard/lib/context-step.mjs +140 -0
- package/wizard/lib/litellm-env.mjs +55 -0
- package/wizard/lib/pm-config.mjs +212 -4
- package/wizard/lib/prompts.mjs +28 -0
- package/wizard/lib/run-wizard.mjs +26 -31
|
@@ -1,16 +1,23 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
3
|
* Install coding-agent session memory for Claude Code, Codex, OpenCode, Cursor CLI.
|
|
4
|
-
* All providers: unified MCP
|
|
5
|
-
*
|
|
4
|
+
* All providers: unified MCP. Session hooks come from OpenMem ([agents] integration =
|
|
5
|
+
* "openmem", default) or the provider's own plugin ("provider", Hindsight only); never both.
|
|
6
|
+
* Optional: rtk command rewriting when rtk is on PATH, and the local context proxy.
|
|
6
7
|
*/
|
|
7
8
|
import { spawnSync } from "node:child_process";
|
|
9
|
+
import fs from "node:fs";
|
|
8
10
|
import path from "node:path";
|
|
9
11
|
import { fileURLToPath } from "node:url";
|
|
10
12
|
import { CODING_AGENTS, DEFAULT_AGENT_IDS } from "./coding-agent/agents.mjs";
|
|
11
13
|
import { AGENT_IDS, PROVIDER_CAPABILITIES } from "./coding-agent/capabilities.mjs";
|
|
12
|
-
import { installSessionShim } from "./coding-agent/install-shim.mjs";
|
|
14
|
+
import { installSessionShim, removeSessionShim, removeUpstreamHooks } from "./coding-agent/install-shim.mjs";
|
|
13
15
|
import { installAgentAccess } from "./coding-agent/install-access.mjs";
|
|
16
|
+
import { contextProxyStartCommand, installContextProxy, probeContextProxy } from "./coding-agent/install-context-proxy.mjs";
|
|
17
|
+
import { upsertEnvFile } from "./coding-agent/file-io.mjs";
|
|
18
|
+
import { installRtk } from "./coding-agent/install-rtk.mjs";
|
|
19
|
+
import { removeUpstreamAccess } from "./coding-agent/remove-upstream-access.mjs";
|
|
20
|
+
import { opencodeMajorVersion } from "./coding-agent/opencode-config.mjs";
|
|
14
21
|
import { prepareAgentRuntime } from "./coding-agent/runtime.mjs";
|
|
15
22
|
import { SHIM_AGENT_IDS } from "./coding-agent/hook-harnesses.mjs";
|
|
16
23
|
import { resolveMemoryProvider } from "./install-memory-mcp.mjs";
|
|
@@ -18,20 +25,44 @@ import { isMainModule } from "./is-main.mjs";
|
|
|
18
25
|
import { resolveHindsightApiUrl } from "./mcp/context.mjs";
|
|
19
26
|
import { resolveStackRoot } from "./stack.mjs";
|
|
20
27
|
import { runInstallMemoryMcp } from "./install-memory-mcp.mjs";
|
|
28
|
+
import { ensureContextProxyService, proxyServiceNeeded } from "./context-management.mjs";
|
|
29
|
+
import {
|
|
30
|
+
agentIntegrationFromPmConfig,
|
|
31
|
+
contextAgentsFromPmConfig,
|
|
32
|
+
contextProxyFromPmConfig,
|
|
33
|
+
contextProxyStackEnvFromPmConfig,
|
|
34
|
+
loadPmConfig,
|
|
35
|
+
updatePmConfigTable,
|
|
36
|
+
} from "../wizard/lib/pm-config.mjs";
|
|
21
37
|
|
|
22
38
|
const PKG_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
23
39
|
const BIN = "openmem-install-coding-agent";
|
|
40
|
+
/** Providers that ship their own coding-agent plugin. */
|
|
41
|
+
const PROVIDER_PLUGINS = ["hindsight"];
|
|
24
42
|
|
|
25
43
|
const HELP = `${BIN}: wire session memory for coding agents
|
|
26
44
|
|
|
27
45
|
Usage:
|
|
28
|
-
npx openmem install-coding-agent [--yes] [--dry-run] [--agents a,b]
|
|
46
|
+
npx openmem install-coding-agent [--yes] [--dry-run] [--agents a,b]
|
|
47
|
+
[--provider-plugin | --no-provider-plugin] [--no-rtk]
|
|
48
|
+
[--context-proxy | --no-context-proxy]
|
|
29
49
|
|
|
30
50
|
Default agents: ${DEFAULT_AGENT_IDS.join(", ")}
|
|
31
51
|
All known agents: ${CODING_AGENTS.map((a) => a.id).join(", ")}
|
|
32
52
|
|
|
33
|
-
Every provider gets install-mcp
|
|
34
|
-
|
|
53
|
+
Every provider gets install-mcp. Session hooks are exclusive:
|
|
54
|
+
default openmem hooks (SessionStart / prompt / Stop); removes upstream Hindsight
|
|
55
|
+
hooks, its "hindsight" MCP server and its hindsight-coding-agent skill
|
|
56
|
+
--provider-plugin the provider's own plugin (Hindsight: hindsight-coding-agents); removes openmem hooks
|
|
57
|
+
--no-provider-plugin switch back to openmem hooks
|
|
58
|
+
The choice is saved as [agents] integration in ~/.config/personal-memory/config.toml.
|
|
59
|
+
--hindsight-plugin was renamed to --provider-plugin.
|
|
60
|
+
|
|
61
|
+
rtk on PATH: runs rtk init for each supported agent (skip with --no-rtk).
|
|
62
|
+
Context proxy: without a flag, [context_proxy.agents] claude-code / opencode1 / codex (missing keys follow
|
|
63
|
+
[context_proxy] enabled) choose which agents go through the local context proxy. With a stack root,
|
|
64
|
+
the installer starts the context-proxy service when one of them is on and stops it when none is.
|
|
65
|
+
--context-proxy / --no-context-proxy override every agent for this run and save [context_proxy] enabled.
|
|
35
66
|
`;
|
|
36
67
|
|
|
37
68
|
function parseArgs(argv) {
|
|
@@ -39,7 +70,9 @@ function parseArgs(argv) {
|
|
|
39
70
|
yes: false,
|
|
40
71
|
dry: false,
|
|
41
72
|
help: false,
|
|
42
|
-
|
|
73
|
+
providerPlugin: undefined,
|
|
74
|
+
rtk: true,
|
|
75
|
+
contextProxy: undefined,
|
|
43
76
|
agents: [...DEFAULT_AGENT_IDS],
|
|
44
77
|
unknown: [],
|
|
45
78
|
};
|
|
@@ -48,7 +81,11 @@ function parseArgs(argv) {
|
|
|
48
81
|
if (a === "--yes" || a === "-y") flags.yes = true;
|
|
49
82
|
else if (a === "--dry-run") flags.dry = true;
|
|
50
83
|
else if (a === "--help" || a === "-h") flags.help = true;
|
|
51
|
-
else if (a === "--
|
|
84
|
+
else if (a === "--provider-plugin") flags.providerPlugin = true;
|
|
85
|
+
else if (a === "--no-provider-plugin") flags.providerPlugin = false;
|
|
86
|
+
else if (a === "--no-rtk") flags.rtk = false;
|
|
87
|
+
else if (a === "--context-proxy") flags.contextProxy = true;
|
|
88
|
+
else if (a === "--no-context-proxy") flags.contextProxy = false;
|
|
52
89
|
else if (a === "--agents") flags.agents = String(argv[++i] || "").split(",").map((s) => s.trim()).filter(Boolean);
|
|
53
90
|
else flags.unknown.push(a);
|
|
54
91
|
}
|
|
@@ -63,38 +100,111 @@ function h() {
|
|
|
63
100
|
};
|
|
64
101
|
}
|
|
65
102
|
|
|
103
|
+
/** Resolve integration and proxy settings from flags, then config; persist flag choices. */
|
|
104
|
+
function resolveSettings({ home, stackRoot, flags, write, log }) {
|
|
105
|
+
const cfg = loadPmConfig(home, stackRoot || home).merged;
|
|
106
|
+
const providerFlag = flags.providerPlugin;
|
|
107
|
+
const integration = providerFlag === undefined ? agentIntegrationFromPmConfig(cfg) : providerFlag ? "provider" : "openmem";
|
|
108
|
+
const proxy = contextProxyFromPmConfig(cfg);
|
|
109
|
+
const contextAgents = contextAgentsFromPmConfig(cfg).agents;
|
|
110
|
+
if (flags.contextProxy !== undefined) {
|
|
111
|
+
proxy.enabled = flags.contextProxy;
|
|
112
|
+
for (const k of Object.keys(contextAgents)) contextAgents[k] = flags.contextProxy;
|
|
113
|
+
}
|
|
114
|
+
if (providerFlag !== undefined) {
|
|
115
|
+
const r = updatePmConfigTable(home, "agents", { integration }, { dry: !write });
|
|
116
|
+
log.info(`${r === "dry-run" ? "dry-run: would save" : "saved"} [agents] integration = "${integration}"`);
|
|
117
|
+
}
|
|
118
|
+
if (flags.contextProxy !== undefined) {
|
|
119
|
+
const r = updatePmConfigTable(home, "context_proxy", { enabled: proxy.enabled }, { dry: !write });
|
|
120
|
+
log.info(`${r === "dry-run" ? "dry-run: would save" : "saved"} [context_proxy] enabled = ${proxy.enabled}`);
|
|
121
|
+
}
|
|
122
|
+
return { integration, proxy, contextAgents, cfg };
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/** Copy config.toml's proxy settings into the stack .env compose reads, when that file exists. */
|
|
126
|
+
function syncProxyStackEnv({ stackRoot, cfg, log }) {
|
|
127
|
+
const entries = contextProxyStackEnvFromPmConfig(cfg);
|
|
128
|
+
const file = stackRoot ? path.join(stackRoot, ".env") : "";
|
|
129
|
+
if (!file || !Object.keys(entries).length || !fs.existsSync(file)) return;
|
|
130
|
+
upsertEnvFile(file, entries);
|
|
131
|
+
log.ok(`context proxy settings → ${file} (${Object.keys(entries).join(", ")})`);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/** Warn loudly when the proxy is not answering: Claude Code fails every request until it runs. */
|
|
135
|
+
async function warnIfProxyDown({ port, startCommand, log, fetch }) {
|
|
136
|
+
if (await probeContextProxy(port, { fetch })) {
|
|
137
|
+
log.ok(`context proxy is up on http://127.0.0.1:${port}`);
|
|
138
|
+
return true;
|
|
139
|
+
}
|
|
140
|
+
const bar = "!".repeat(72);
|
|
141
|
+
log.warn([
|
|
142
|
+
bar,
|
|
143
|
+
`! Context proxy is NOT running on http://127.0.0.1:${port}.`,
|
|
144
|
+
"! Claude Code (and OpenCode 1) now send every request there and will fail until it runs.",
|
|
145
|
+
`! Start it: ${startCommand}`,
|
|
146
|
+
"! Or disable: npx openmem install-coding-agent --yes --no-context-proxy",
|
|
147
|
+
bar,
|
|
148
|
+
].join("\n"));
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
151
|
+
|
|
66
152
|
export async function runInstallCodingAgent(opts) {
|
|
67
153
|
const home = opts.home;
|
|
68
154
|
const env = opts.env || process.env;
|
|
69
155
|
const flags = opts.flags;
|
|
156
|
+
const log = opts.h || h();
|
|
70
157
|
for (const agentId of flags.agents) {
|
|
71
158
|
if (!AGENT_IDS.includes(agentId)) {
|
|
72
|
-
|
|
159
|
+
log.warn(`Unknown agent "${agentId}" (known: ${AGENT_IDS.join(", ")})`);
|
|
73
160
|
}
|
|
74
161
|
}
|
|
75
162
|
const provider = resolveMemoryProvider(home, env);
|
|
76
163
|
const caps = PROVIDER_CAPABILITIES[provider] || {};
|
|
77
|
-
|
|
164
|
+
log.info(`Provider ${provider}: capabilities ${JSON.stringify(caps)}`);
|
|
78
165
|
|
|
166
|
+
const dry = flags.dry || !flags.yes;
|
|
167
|
+
const { integration, proxy, contextAgents, cfg } = resolveSettings({ home, stackRoot: opts.stackRoot, flags, write: !dry, log });
|
|
168
|
+
const project = env.PM_PROJECT || String(cfg.stack?.project || "") || "openmem";
|
|
169
|
+
const startCommand = contextProxyStartCommand({ stackRoot: opts.stackRoot, project });
|
|
170
|
+
// OpenCode 2 never uses the proxy; its plugin reads [context_proxy.agents] opencode2 itself.
|
|
171
|
+
const agentEnabled = { "claude-code": contextAgents["claude-code"], opencode: contextAgents.opencode1, codex: contextAgents.codex };
|
|
172
|
+
const serviceOn = proxyServiceNeeded(contextAgents);
|
|
173
|
+
const service = (dryRun) => ensureContextProxyService({
|
|
174
|
+
home, stackRoot: opts.stackRoot, project, on: serviceOn, dry: dryRun, env, spawn: opts.docker, log,
|
|
175
|
+
});
|
|
176
|
+
const useProviderPlugin = integration === "provider" && PROVIDER_PLUGINS.includes(provider);
|
|
177
|
+
if (integration === "provider" && !useProviderPlugin) {
|
|
178
|
+
log.warn(`Provider ${provider} has no coding-agent plugin; installing openmem hooks instead`);
|
|
179
|
+
}
|
|
79
180
|
const shimAgents = flags.agents.filter((id) => SHIM_AGENT_IDS.includes(id) || id === "opencode");
|
|
181
|
+
const opencodeMajor = flags.agents.includes("opencode") ? opencodeMajorVersion({ env }) : 1;
|
|
80
182
|
const runtimeRoot = prepareAgentRuntime({
|
|
81
|
-
home, repoRoot: opts.repoRoot, stackRoot: opts.stackRoot, dry
|
|
183
|
+
home, repoRoot: opts.repoRoot, stackRoot: opts.stackRoot, dry, h: log,
|
|
82
184
|
});
|
|
185
|
+
const upstreamCmd = () => {
|
|
186
|
+
const apiUrl = resolveHindsightApiUrl({ home, stackRoot: opts.stackRoot, env });
|
|
187
|
+
return ["npx", "-y", "@vectorize-io/hindsight-coding-agents", "install", ...flags.agents, "--server", "self-hosted", "--api-url", apiUrl];
|
|
188
|
+
};
|
|
83
189
|
if (flags.dry) {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
);
|
|
190
|
+
log.info("dry-run: would run install-mcp --yes");
|
|
191
|
+
if (useProviderPlugin) {
|
|
192
|
+
removeSessionShim({ home, agents: shimAgents, dry: true, h: log });
|
|
193
|
+
} else {
|
|
194
|
+
installSessionShim({ home, repoRoot: runtimeRoot, agents: shimAgents, dry: true, h: log, opencodeMajor });
|
|
195
|
+
removeUpstreamHooks({ home, agents: shimAgents, dry: true, h: log });
|
|
196
|
+
removeUpstreamAccess({ home, agents: flags.agents, dry: true, h: log });
|
|
92
197
|
}
|
|
93
|
-
|
|
198
|
+
installAgentAccess({ home, runtimeRoot, stackRoot: opts.stackRoot, agents: flags.agents, dry: true, h: log });
|
|
199
|
+
if (flags.rtk) installRtk({ agents: flags.agents, env, dry: true, h: log, opencodeMajor });
|
|
200
|
+
installContextProxy({ home, agents: flags.agents, enabled: proxy.enabled, agentEnabled, port: proxy.port, dry: true, h: log, opencodeMajor });
|
|
201
|
+
service(true);
|
|
202
|
+
if (useProviderPlugin) log.info(`dry-run: would also run ${upstreamCmd().join(" ")}`);
|
|
203
|
+
return { provider, integration, ran: false };
|
|
94
204
|
}
|
|
95
205
|
if (!flags.yes) {
|
|
96
|
-
|
|
97
|
-
return { provider, ran: false };
|
|
206
|
+
log.warn("Re-run with --yes to install MCP + session hooks");
|
|
207
|
+
return { provider, integration, ran: false };
|
|
98
208
|
}
|
|
99
209
|
|
|
100
210
|
await runInstallMemoryMcp({
|
|
@@ -103,40 +213,63 @@ export async function runInstallCodingAgent(opts) {
|
|
|
103
213
|
stackRoot: opts.stackRoot,
|
|
104
214
|
flags: { yes: true, dry: false, notebooklm: false, notebooklmOnly: false, providerOnly: false, noUnifiedMcp: false, help: false, unknown: [] },
|
|
105
215
|
env,
|
|
106
|
-
h:
|
|
107
|
-
});
|
|
108
|
-
installSessionShim({
|
|
109
|
-
home,
|
|
110
|
-
repoRoot: runtimeRoot,
|
|
111
|
-
agents: shimAgents,
|
|
112
|
-
dry: false,
|
|
113
|
-
h: opts.h || h(),
|
|
216
|
+
h: log,
|
|
114
217
|
});
|
|
218
|
+
if (useProviderPlugin) {
|
|
219
|
+
removeSessionShim({ home, agents: shimAgents, h: log });
|
|
220
|
+
} else {
|
|
221
|
+
installSessionShim({ home, repoRoot: runtimeRoot, agents: shimAgents, dry: false, h: log, opencodeMajor });
|
|
222
|
+
removeUpstreamHooks({ home, agents: shimAgents, h: log });
|
|
223
|
+
removeUpstreamAccess({ home, agents: flags.agents, h: log });
|
|
224
|
+
}
|
|
115
225
|
installAgentAccess({
|
|
116
|
-
home, runtimeRoot, stackRoot: opts.stackRoot, agents: flags.agents, dry: false, h:
|
|
226
|
+
home, runtimeRoot, stackRoot: opts.stackRoot, agents: flags.agents, dry: false, h: log,
|
|
117
227
|
});
|
|
228
|
+
const rtk = flags.rtk ? installRtk({ agents: flags.agents, env, h: log, spawn: opts.spawn, opencodeMajor }) : { rtk: null, ran: [] };
|
|
229
|
+
// On: start the service before agents point at it. Off: stop it after they are unwired.
|
|
230
|
+
if (serviceOn) service(false);
|
|
231
|
+
const proxyOut = installContextProxy({ home, agents: flags.agents, enabled: proxy.enabled, agentEnabled, port: proxy.port, h: log, opencodeMajor });
|
|
232
|
+
if (!serviceOn) service(false);
|
|
233
|
+
const proxyUsed = flags.agents.some((id) => id in agentEnabled && agentEnabled[id] && !proxyOut.pluginTrimmed.includes(id));
|
|
234
|
+
if (proxyUsed) {
|
|
235
|
+
syncProxyStackEnv({ stackRoot: opts.stackRoot, cfg, log });
|
|
236
|
+
await warnIfProxyDown({ port: proxy.port, startCommand, log, fetch: opts.fetch });
|
|
237
|
+
}
|
|
118
238
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
const cmd =
|
|
122
|
-
|
|
123
|
-
const r = spawnSync(cmd[0], cmd.slice(1), { stdio: "inherit", env });
|
|
124
|
-
|
|
239
|
+
let status = 0;
|
|
240
|
+
if (useProviderPlugin) {
|
|
241
|
+
const cmd = upstreamCmd();
|
|
242
|
+
log.info(`Installing upstream Hindsight plugin: ${cmd.join(" ")}`);
|
|
243
|
+
const r = (opts.spawn || spawnSync)(cmd[0], cmd.slice(1), { stdio: "inherit", env });
|
|
244
|
+
status = r.status ?? 1;
|
|
125
245
|
}
|
|
126
246
|
|
|
127
247
|
for (const agentId of flags.agents) {
|
|
128
248
|
if (shimAgents.includes(agentId)) {
|
|
129
|
-
|
|
249
|
+
const hooks = useProviderPlugin ? "upstream Hindsight hooks" : "openmem session hooks";
|
|
250
|
+
const extras = [
|
|
251
|
+
rtk.ran.includes(agentId) ? "rtk" : "",
|
|
252
|
+
proxyOut.changed.includes(agentId) && agentEnabled[agentId] ? "context proxy" : "",
|
|
253
|
+
!useProviderPlugin && proxyOut.pluginTrimmed.includes(agentId) ? "native context trimming (OpenCode 2; context proxy not used)" : "",
|
|
254
|
+
].filter(Boolean);
|
|
255
|
+
log.info(` ${agentId}: ${hooks} + unified MCP${extras.length ? ` + ${extras.join(" + ")}` : ""}`);
|
|
130
256
|
} else {
|
|
131
257
|
const row = CODING_AGENTS.find((a) => a.id === agentId);
|
|
132
|
-
|
|
258
|
+
log.info(` ${agentId}: ${row?.sessionPath || "unified MCP memory_*"}`);
|
|
133
259
|
}
|
|
134
260
|
}
|
|
135
|
-
|
|
261
|
+
if (agentEnabled["claude-code"] && flags.agents.includes("claude-code")) {
|
|
262
|
+
log.info(`Context proxy enabled: Claude Code now sends every request to http://127.0.0.1:${proxy.port}; start the proxy before using Claude Code: ${startCommand}`);
|
|
263
|
+
}
|
|
264
|
+
return { provider, integration, ran: true, status };
|
|
136
265
|
}
|
|
137
266
|
|
|
138
267
|
async function main() {
|
|
139
268
|
const flags = parseArgs(process.argv.slice(2));
|
|
269
|
+
if (flags.unknown.includes("--hindsight-plugin")) {
|
|
270
|
+
console.error("install-coding-agent: --hindsight-plugin was renamed to --provider-plugin");
|
|
271
|
+
process.exit(2);
|
|
272
|
+
}
|
|
140
273
|
if (flags.help || flags.unknown.length) {
|
|
141
274
|
console.log(HELP);
|
|
142
275
|
process.exit(flags.unknown.length ? 2 : 0);
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Child process for smart_compact_sql (cli/mcp/context-store.mjs): reads {file, sql, cap, cellMax} as JSON on
|
|
3
|
+
* stdin, runs the statement on a read-only connection and prints {columns, rows, truncated} or {error}. The parent
|
|
4
|
+
* kills it after its time limit, which also stops a long-running SQLite step.
|
|
5
|
+
*/
|
|
6
|
+
import fs from "node:fs";
|
|
7
|
+
|
|
8
|
+
const SQLITE_OK = 0;
|
|
9
|
+
const SQLITE_DENY = 1;
|
|
10
|
+
// sqlite3_set_authorizer action codes allowed for a query: SELECT, READ, FUNCTION, RECURSIVE.
|
|
11
|
+
const ALLOWED_ACTIONS = new Set([21, 20, 31, 33]);
|
|
12
|
+
// SQLITE_PRAGMA; FTS5 reads `PRAGMA data_version` internally on a MATCH. User SQL never reaches here with a
|
|
13
|
+
// PRAGMA (the parent's statement check rejects it), and an assignment carries a value in arg2.
|
|
14
|
+
const SQLITE_PRAGMA = 19;
|
|
15
|
+
const READ_PRAGMAS = new Set(["data_version"]);
|
|
16
|
+
|
|
17
|
+
function cell(value, cellMax) {
|
|
18
|
+
if (value instanceof Uint8Array) return `<blob ${value.byteLength} bytes>`;
|
|
19
|
+
if (typeof value === "bigint") return Number.isSafeInteger(Number(value)) ? Number(value) : String(value);
|
|
20
|
+
if (typeof value === "string" && value.length > cellMax) return `${value.slice(0, cellMax)}…`;
|
|
21
|
+
return value;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function run({ file, sql, cap, cellMax }) {
|
|
25
|
+
const { DatabaseSync } = process.getBuiltinModule("node:sqlite");
|
|
26
|
+
const db = new DatabaseSync(file, { readOnly: true });
|
|
27
|
+
try {
|
|
28
|
+
db.exec("PRAGMA busy_timeout = 1000");
|
|
29
|
+
// Node >= 24.10 (setAuthorizer): deny every action a query does not need, on top of the read-only
|
|
30
|
+
// connection and the parent's statement check.
|
|
31
|
+
if (typeof db.setAuthorizer === "function") {
|
|
32
|
+
db.setAuthorizer((action, arg1, arg2) => (ALLOWED_ACTIONS.has(action)
|
|
33
|
+
|| (action === SQLITE_PRAGMA && READ_PRAGMAS.has(String(arg1).toLowerCase()) && arg2 === null) ? SQLITE_OK : SQLITE_DENY));
|
|
34
|
+
}
|
|
35
|
+
const rows = [];
|
|
36
|
+
let truncated = false;
|
|
37
|
+
let columns = [];
|
|
38
|
+
for (const row of db.prepare(sql).iterate()) {
|
|
39
|
+
if (rows.length === cap) {
|
|
40
|
+
truncated = true;
|
|
41
|
+
break;
|
|
42
|
+
}
|
|
43
|
+
if (!columns.length) columns = Object.keys(row);
|
|
44
|
+
rows.push(Object.fromEntries(Object.entries(row).map(([k, v]) => [k, cell(v, cellMax)])));
|
|
45
|
+
}
|
|
46
|
+
return { columns, rows, truncated };
|
|
47
|
+
} finally {
|
|
48
|
+
db.close();
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
let out;
|
|
53
|
+
try {
|
|
54
|
+
out = run(JSON.parse(fs.readFileSync(0, "utf8")));
|
|
55
|
+
} catch (err) {
|
|
56
|
+
out = { error: err instanceof Error ? err.message : String(err) };
|
|
57
|
+
}
|
|
58
|
+
process.stdout.write(JSON.stringify(out));
|