hippo-memory 0.35.0 → 0.36.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/README.md +9 -0
- package/dist/api.d.ts +163 -0
- package/dist/api.d.ts.map +1 -0
- package/dist/api.js +323 -0
- package/dist/api.js.map +1 -0
- package/dist/cli.js +140 -34
- package/dist/cli.js.map +1 -1
- package/dist/client.d.ts +54 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +181 -0
- package/dist/client.js.map +1 -0
- package/dist/mcp/server.d.ts +46 -1
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +74 -26
- package/dist/mcp/server.js.map +1 -1
- package/dist/server-detect.d.ts +26 -0
- package/dist/server-detect.d.ts.map +1 -0
- package/dist/server-detect.js +70 -0
- package/dist/server-detect.js.map +1 -0
- package/dist/server.d.ts +29 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +612 -0
- package/dist/server.js.map +1 -0
- package/dist/shared.d.ts +3 -1
- package/dist/shared.d.ts.map +1 -1
- package/dist/shared.js +2 -2
- package/dist/shared.js.map +1 -1
- package/dist/store.d.ts +15 -2
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +20 -9
- package/dist/store.js.map +1 -1
- package/extensions/openclaw-plugin/openclaw.plugin.json +1 -1
- package/extensions/openclaw-plugin/package.json +1 -1
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -45,12 +45,15 @@ import { captureError, extractLessons, deduplicateLesson, runWatched, fetchGitLo
|
|
|
45
45
|
import { extractInvalidationTarget, invalidateMatching } from './invalidation.js';
|
|
46
46
|
import { extractPathTags } from './path-context.js';
|
|
47
47
|
import { detectScope, scopeMatch } from './scope.js';
|
|
48
|
-
import { getGlobalRoot, initGlobal,
|
|
48
|
+
import { getGlobalRoot, initGlobal, shareMemory, listPeers, autoShare, transferScore, searchBothHybrid, syncGlobalToLocal, } from './shared.js';
|
|
49
49
|
import { DAILY_TASK_NAME, buildDailyRunnerCommand, listRegisteredWorkspaces, registerWorkspace, runDailyMaintenance, } from './scheduler.js';
|
|
50
50
|
import { importChatGPT, importClaude, importCursor, importGenericFile, importMarkdown, } from './importers.js';
|
|
51
51
|
import { cmdCapture } from './capture.js';
|
|
52
|
-
import { auditMemories, appendAuditEvent,
|
|
53
|
-
import {
|
|
52
|
+
import { auditMemories, appendAuditEvent, } from './audit.js';
|
|
53
|
+
import { listApiKeys, revokeApiKey } from './auth.js';
|
|
54
|
+
import * as api from './api.js';
|
|
55
|
+
import * as client from './client.js';
|
|
56
|
+
import { detectServer, removePidfile } from './server-detect.js';
|
|
54
57
|
import { resolveTenantId } from './tenant.js';
|
|
55
58
|
import { runEval, bootstrapCorpus, compareSummaries } from './eval.js';
|
|
56
59
|
import { runFeatureEval, formatResult, resultToBaseline, detectRegressions } from './eval-suite.js';
|
|
@@ -99,6 +102,35 @@ function requireInit(hippoRoot) {
|
|
|
99
102
|
process.exit(1);
|
|
100
103
|
}
|
|
101
104
|
}
|
|
105
|
+
/**
|
|
106
|
+
* Run an HTTP-routed command if a `hippo serve` instance is detected for
|
|
107
|
+
* `hippoRoot`. Returns:
|
|
108
|
+
* - true if the HTTP path ran (success OR a structured server error that
|
|
109
|
+
* was already surfaced to stdout/stderr by `httpFn`),
|
|
110
|
+
* - false if no server was detected, or if the detected pidfile turned out
|
|
111
|
+
* to be stale (connection refused). On stale, the pidfile is removed
|
|
112
|
+
* and the caller should fall back to the direct path.
|
|
113
|
+
*
|
|
114
|
+
* Per the A1 plan footgun #1: stale pidfiles must self-heal, not crash.
|
|
115
|
+
*/
|
|
116
|
+
async function runViaServerIfAvailable(hippoRoot, httpFn) {
|
|
117
|
+
const info = detectServer(hippoRoot);
|
|
118
|
+
if (!info)
|
|
119
|
+
return false;
|
|
120
|
+
const apiKey = process.env['HIPPO_API_KEY'];
|
|
121
|
+
try {
|
|
122
|
+
await httpFn(info, apiKey);
|
|
123
|
+
return true;
|
|
124
|
+
}
|
|
125
|
+
catch (err) {
|
|
126
|
+
if (client.isConnectionRefused(err)) {
|
|
127
|
+
console.error('hippo: stale server pidfile detected, falling back to direct mode');
|
|
128
|
+
removePidfile(hippoRoot);
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
throw err;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
102
134
|
function parseArgs(argv) {
|
|
103
135
|
const [, , command = '', ...rest] = argv;
|
|
104
136
|
const args = [];
|
|
@@ -2159,12 +2191,17 @@ function cmdOutcome(hippoRoot, flags) {
|
|
|
2159
2191
|
}
|
|
2160
2192
|
function cmdForget(hippoRoot, id) {
|
|
2161
2193
|
requireInit(hippoRoot);
|
|
2162
|
-
const
|
|
2163
|
-
|
|
2194
|
+
const ctx = {
|
|
2195
|
+
hippoRoot,
|
|
2196
|
+
tenantId: resolveTenantId({}),
|
|
2197
|
+
actor: 'cli',
|
|
2198
|
+
};
|
|
2199
|
+
try {
|
|
2200
|
+
api.forget(ctx, id);
|
|
2164
2201
|
updateStats(hippoRoot, { forgotten: 1 });
|
|
2165
2202
|
console.log(`Forgot ${id}`);
|
|
2166
2203
|
}
|
|
2167
|
-
|
|
2204
|
+
catch {
|
|
2168
2205
|
console.error(`Memory not found: ${id}`);
|
|
2169
2206
|
process.exit(1);
|
|
2170
2207
|
}
|
|
@@ -3238,14 +3275,14 @@ function cmdPromote(hippoRoot, id) {
|
|
|
3238
3275
|
console.error('Usage: hippo promote <id>');
|
|
3239
3276
|
process.exit(1);
|
|
3240
3277
|
}
|
|
3278
|
+
const ctx = {
|
|
3279
|
+
hippoRoot,
|
|
3280
|
+
tenantId: resolveTenantId({}),
|
|
3281
|
+
actor: 'cli',
|
|
3282
|
+
};
|
|
3241
3283
|
try {
|
|
3242
|
-
const
|
|
3243
|
-
|
|
3244
|
-
// The writeEntry inside promoteToGlobal already fires a 'remember' on the
|
|
3245
|
-
// global db; we add a separate 'promote' event so the audit trail keeps
|
|
3246
|
-
// the user-facing intent distinct from the underlying upsert.
|
|
3247
|
-
emitCliAudit(getGlobalRoot(), 'promote', globalEntry.id, { sourceId: id });
|
|
3248
|
-
console.log(`Promoted ${id} to global store as ${globalEntry.id}`);
|
|
3284
|
+
const result = api.promote(ctx, id);
|
|
3285
|
+
console.log(`Promoted ${id} to global store as ${result.globalId}`);
|
|
3249
3286
|
console.log(` Global store: ${getGlobalRoot()}`);
|
|
3250
3287
|
}
|
|
3251
3288
|
catch (err) {
|
|
@@ -3813,21 +3850,18 @@ function cmdAuthCreate(hippoRoot, flags) {
|
|
|
3813
3850
|
const root = resolveAuthRoot(hippoRoot, flags);
|
|
3814
3851
|
const tenantFlag = typeof flags['tenant'] === 'string' ? flags['tenant'] : undefined;
|
|
3815
3852
|
const labelFlag = typeof flags['label'] === 'string' ? flags['label'] : undefined;
|
|
3816
|
-
const tenantId = tenantFlag ?? resolveTenantId({});
|
|
3817
3853
|
const asJson = Boolean(flags['json']);
|
|
3818
|
-
const
|
|
3819
|
-
|
|
3820
|
-
|
|
3821
|
-
|
|
3822
|
-
}
|
|
3823
|
-
|
|
3824
|
-
closeHippoDb(db);
|
|
3825
|
-
}
|
|
3854
|
+
const ctx = {
|
|
3855
|
+
hippoRoot: root,
|
|
3856
|
+
tenantId: resolveTenantId({}),
|
|
3857
|
+
actor: 'cli',
|
|
3858
|
+
};
|
|
3859
|
+
const result = api.authCreate(ctx, { tenantId: tenantFlag, label: labelFlag });
|
|
3826
3860
|
if (asJson) {
|
|
3827
3861
|
console.log(JSON.stringify({
|
|
3828
3862
|
keyId: result.keyId,
|
|
3829
3863
|
plaintext: result.plaintext,
|
|
3830
|
-
tenantId,
|
|
3864
|
+
tenantId: result.tenantId,
|
|
3831
3865
|
label: labelFlag ?? null,
|
|
3832
3866
|
}));
|
|
3833
3867
|
return;
|
|
@@ -3969,14 +4003,8 @@ function cmdAuditList(hippoRoot, flags) {
|
|
|
3969
4003
|
console.error(`--limit must be between 1 and 10000 (got ${limit}).`);
|
|
3970
4004
|
process.exit(1);
|
|
3971
4005
|
}
|
|
3972
|
-
const
|
|
3973
|
-
|
|
3974
|
-
try {
|
|
3975
|
-
events = queryAuditEvents(db, { tenantId, op, since, limit });
|
|
3976
|
-
}
|
|
3977
|
-
finally {
|
|
3978
|
-
closeHippoDb(db);
|
|
3979
|
-
}
|
|
4006
|
+
const ctx = { hippoRoot: root, tenantId, actor: 'cli' };
|
|
4007
|
+
const events = api.auditList(ctx, { op, since, limit });
|
|
3980
4008
|
if (asJson) {
|
|
3981
4009
|
console.log(JSON.stringify(events));
|
|
3982
4010
|
return;
|
|
@@ -4350,6 +4378,37 @@ async function main() {
|
|
|
4350
4378
|
console.error('Memory content too short (minimum 3 characters).');
|
|
4351
4379
|
process.exit(1);
|
|
4352
4380
|
}
|
|
4381
|
+
// Thin-client routing. When a server is up, simple `remember` calls go
|
|
4382
|
+
// over HTTP so the daemon stays single-writer (footgun #2). Rich CLI
|
|
4383
|
+
// flags (--pin, --layer, --extract, --global, salience gates) still
|
|
4384
|
+
// need the direct path; we only intercept the minimal envelope.
|
|
4385
|
+
const richFlag = flags['pin'] || flags['global'] || flags['extract'] || flags['force'] ||
|
|
4386
|
+
flags['observed'] || flags['inferred'] || flags['verified'] ||
|
|
4387
|
+
flags['layer'] !== undefined;
|
|
4388
|
+
if (!richFlag) {
|
|
4389
|
+
const rememberKindRaw = typeof flags['kind'] === 'string' ? flags['kind'].toLowerCase() : undefined;
|
|
4390
|
+
const rememberKindAllowed = ['distilled', 'superseded'];
|
|
4391
|
+
if (rememberKindRaw === undefined || rememberKindAllowed.includes(rememberKindRaw)) {
|
|
4392
|
+
const tagsRaw = flags['tag'];
|
|
4393
|
+
const tags = Array.isArray(tagsRaw)
|
|
4394
|
+
? tagsRaw.map(String)
|
|
4395
|
+
: typeof tagsRaw === 'string' ? [tagsRaw] : undefined;
|
|
4396
|
+
const remembered = await runViaServerIfAvailable(hippoRoot, async (info, apiKey) => {
|
|
4397
|
+
const result = await client.remember(info.url, apiKey, {
|
|
4398
|
+
content: text,
|
|
4399
|
+
kind: rememberKindRaw,
|
|
4400
|
+
scope: typeof flags['scope'] === 'string' ? flags['scope'] : undefined,
|
|
4401
|
+
owner: typeof flags['owner'] === 'string' ? flags['owner'] : undefined,
|
|
4402
|
+
artifactRef: typeof flags['artifact-ref'] === 'string' ? flags['artifact-ref'] : undefined,
|
|
4403
|
+
tags,
|
|
4404
|
+
});
|
|
4405
|
+
console.log(`Remembered [${result.id}] (via ${info.url})`);
|
|
4406
|
+
console.log(` Kind: ${result.kind} | Tenant: ${result.tenantId}`);
|
|
4407
|
+
});
|
|
4408
|
+
if (remembered)
|
|
4409
|
+
break;
|
|
4410
|
+
}
|
|
4411
|
+
}
|
|
4353
4412
|
await cmdRemember(hippoRoot, text, flags);
|
|
4354
4413
|
break;
|
|
4355
4414
|
}
|
|
@@ -4499,6 +4558,18 @@ async function main() {
|
|
|
4499
4558
|
console.error('Please provide a memory ID.');
|
|
4500
4559
|
process.exit(1);
|
|
4501
4560
|
}
|
|
4561
|
+
const routed = await runViaServerIfAvailable(hippoRoot, async (info, apiKey) => {
|
|
4562
|
+
try {
|
|
4563
|
+
await client.forget(info.url, apiKey, id);
|
|
4564
|
+
console.log(`Forgot ${id}`);
|
|
4565
|
+
}
|
|
4566
|
+
catch (err) {
|
|
4567
|
+
console.error(err.message);
|
|
4568
|
+
process.exit(1);
|
|
4569
|
+
}
|
|
4570
|
+
});
|
|
4571
|
+
if (routed)
|
|
4572
|
+
break;
|
|
4502
4573
|
cmdForget(hippoRoot, id);
|
|
4503
4574
|
break;
|
|
4504
4575
|
}
|
|
@@ -4540,6 +4611,18 @@ async function main() {
|
|
|
4540
4611
|
console.error('Please provide a memory ID.');
|
|
4541
4612
|
process.exit(1);
|
|
4542
4613
|
}
|
|
4614
|
+
const promoted = await runViaServerIfAvailable(hippoRoot, async (info, apiKey) => {
|
|
4615
|
+
try {
|
|
4616
|
+
const result = await client.promote(info.url, apiKey, id);
|
|
4617
|
+
console.log(`Promoted ${id} to global store as ${result.globalId}`);
|
|
4618
|
+
}
|
|
4619
|
+
catch (err) {
|
|
4620
|
+
console.error(`Failed to promote: ${err.message}`);
|
|
4621
|
+
process.exit(1);
|
|
4622
|
+
}
|
|
4623
|
+
});
|
|
4624
|
+
if (promoted)
|
|
4625
|
+
break;
|
|
4543
4626
|
cmdPromote(hippoRoot, id);
|
|
4544
4627
|
break;
|
|
4545
4628
|
}
|
|
@@ -4689,12 +4772,35 @@ async function main() {
|
|
|
4689
4772
|
case 'wm':
|
|
4690
4773
|
cmdWm(hippoRoot, args, flags);
|
|
4691
4774
|
break;
|
|
4692
|
-
case 'mcp':
|
|
4693
|
-
// Start MCP server over stdio
|
|
4694
|
-
|
|
4775
|
+
case 'mcp': {
|
|
4776
|
+
// Start MCP server over stdio. Dynamic import keeps main CLI lean; the
|
|
4777
|
+
// dispatcher itself is transport-agnostic, so we explicitly attach the
|
|
4778
|
+
// stdio loop here. (HTTP/SSE transport is wired in src/server.ts and
|
|
4779
|
+
// imports the same module without triggering stdin handlers.)
|
|
4780
|
+
const mod = await import('./mcp/server.js');
|
|
4781
|
+
mod.startStdioLoop();
|
|
4695
4782
|
// Server runs until stdin closes, so we never reach here
|
|
4696
4783
|
await new Promise(() => { }); // hang forever
|
|
4697
4784
|
break;
|
|
4785
|
+
}
|
|
4786
|
+
case 'serve': {
|
|
4787
|
+
requireInit(hippoRoot);
|
|
4788
|
+
const portRaw = flags['port'] ?? process.env['HIPPO_PORT'] ?? '6789';
|
|
4789
|
+
const port = Number(portRaw);
|
|
4790
|
+
if (!Number.isFinite(port) || port < 0) {
|
|
4791
|
+
console.error(`Invalid --port: ${String(portRaw)}`);
|
|
4792
|
+
process.exit(1);
|
|
4793
|
+
}
|
|
4794
|
+
const host = typeof flags['host'] === 'string' ? flags['host'] : '127.0.0.1';
|
|
4795
|
+
const { serve } = await import('./server.js');
|
|
4796
|
+
const handle = await serve({ hippoRoot, port, host });
|
|
4797
|
+
console.log(`hippo serve listening on ${handle.url} (pid ${process.pid})`);
|
|
4798
|
+
console.log(`pidfile: ${path.join(hippoRoot, 'server.pid')}`);
|
|
4799
|
+
console.log('press Ctrl+C to stop');
|
|
4800
|
+
// SIGINT/SIGTERM handlers wired in server.ts (skipped under VITEST). Hang.
|
|
4801
|
+
await new Promise(() => { });
|
|
4802
|
+
break;
|
|
4803
|
+
}
|
|
4698
4804
|
case 'invalidate': {
|
|
4699
4805
|
requireInit(hippoRoot);
|
|
4700
4806
|
const target = args[0];
|