hippo-memory 0.35.0 → 0.37.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 +25 -0
- package/dist/api.d.ts +183 -0
- package/dist/api.d.ts.map +1 -0
- package/dist/api.js +343 -0
- package/dist/api.js.map +1 -0
- package/dist/benchmarks/e1.3/incident-recall-eval.js +74 -0
- package/dist/benchmarks/e1.3/incident-recall-eval.js.map +1 -0
- package/dist/benchmarks/e1.3/scenarios.json +2587 -0
- package/dist/benchmarks/e1.3/slack-1000-event-smoke.js +102 -0
- package/dist/benchmarks/e1.3/slack-1000-event-smoke.js.map +1 -0
- package/dist/cli.js +222 -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/connectors/slack/backfill.d.ts +42 -0
- package/dist/connectors/slack/backfill.d.ts.map +1 -0
- package/dist/connectors/slack/backfill.js +76 -0
- package/dist/connectors/slack/backfill.js.map +1 -0
- package/dist/connectors/slack/deletion.d.ts +14 -0
- package/dist/connectors/slack/deletion.d.ts.map +1 -0
- package/dist/connectors/slack/deletion.js +46 -0
- package/dist/connectors/slack/deletion.js.map +1 -0
- package/dist/connectors/slack/dlq.d.ts +21 -0
- package/dist/connectors/slack/dlq.d.ts.map +1 -0
- package/dist/connectors/slack/dlq.js +23 -0
- package/dist/connectors/slack/dlq.js.map +1 -0
- package/dist/connectors/slack/idempotency.d.ts +5 -0
- package/dist/connectors/slack/idempotency.d.ts.map +1 -0
- package/dist/connectors/slack/idempotency.js +13 -0
- package/dist/connectors/slack/idempotency.js.map +1 -0
- package/dist/connectors/slack/ingest.d.ts +27 -0
- package/dist/connectors/slack/ingest.d.ts.map +1 -0
- package/dist/connectors/slack/ingest.js +48 -0
- package/dist/connectors/slack/ingest.js.map +1 -0
- package/dist/connectors/slack/ratelimit.d.ts +9 -0
- package/dist/connectors/slack/ratelimit.d.ts.map +1 -0
- package/dist/connectors/slack/ratelimit.js +18 -0
- package/dist/connectors/slack/ratelimit.js.map +1 -0
- package/dist/connectors/slack/scope.d.ts +16 -0
- package/dist/connectors/slack/scope.d.ts.map +1 -0
- package/dist/connectors/slack/scope.js +13 -0
- package/dist/connectors/slack/scope.js.map +1 -0
- package/dist/connectors/slack/signature.d.ts +12 -0
- package/dist/connectors/slack/signature.d.ts.map +1 -0
- package/dist/connectors/slack/signature.js +20 -0
- package/dist/connectors/slack/signature.js.map +1 -0
- package/dist/connectors/slack/tenant-routing.d.ts +13 -0
- package/dist/connectors/slack/tenant-routing.d.ts.map +1 -0
- package/dist/connectors/slack/tenant-routing.js +17 -0
- package/dist/connectors/slack/tenant-routing.js.map +1 -0
- package/dist/connectors/slack/transform.d.ts +20 -0
- package/dist/connectors/slack/transform.d.ts.map +1 -0
- package/dist/connectors/slack/transform.js +31 -0
- package/dist/connectors/slack/transform.js.map +1 -0
- package/dist/connectors/slack/types.d.ts +35 -0
- package/dist/connectors/slack/types.d.ts.map +1 -0
- package/dist/connectors/slack/types.js +23 -0
- package/dist/connectors/slack/types.js.map +1 -0
- package/dist/connectors/slack/web-client.d.ts +12 -0
- package/dist/connectors/slack/web-client.d.ts.map +1 -0
- package/dist/connectors/slack/web-client.js +43 -0
- package/dist/connectors/slack/web-client.js.map +1 -0
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +46 -1
- package/dist/db.js.map +1 -1
- package/dist/importers.js +3 -3
- package/dist/importers.js.map +1 -1
- 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 +784 -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/src/ambient.js +147 -0
- package/dist/src/ambient.js.map +1 -0
- package/dist/src/api.js +343 -0
- package/dist/src/api.js.map +1 -0
- package/dist/src/audit.js +152 -0
- package/dist/src/audit.js.map +1 -0
- package/dist/src/auth.js +65 -0
- package/dist/src/auth.js.map +1 -0
- package/dist/src/autolearn.js +143 -0
- package/dist/src/autolearn.js.map +1 -0
- package/dist/src/capture.js +512 -0
- package/dist/src/capture.js.map +1 -0
- package/dist/src/cli.js +4971 -0
- package/dist/src/cli.js.map +1 -0
- package/dist/src/client.js +181 -0
- package/dist/src/client.js.map +1 -0
- package/dist/src/config.js +108 -0
- package/dist/src/config.js.map +1 -0
- package/dist/src/connectors/slack/backfill.js +76 -0
- package/dist/src/connectors/slack/backfill.js.map +1 -0
- package/dist/src/connectors/slack/deletion.js +46 -0
- package/dist/src/connectors/slack/deletion.js.map +1 -0
- package/dist/src/connectors/slack/dlq.js +23 -0
- package/dist/src/connectors/slack/dlq.js.map +1 -0
- package/dist/src/connectors/slack/idempotency.js +13 -0
- package/dist/src/connectors/slack/idempotency.js.map +1 -0
- package/dist/src/connectors/slack/ingest.js +48 -0
- package/dist/src/connectors/slack/ingest.js.map +1 -0
- package/dist/src/connectors/slack/ratelimit.js +18 -0
- package/dist/src/connectors/slack/ratelimit.js.map +1 -0
- package/dist/src/connectors/slack/scope.js +13 -0
- package/dist/src/connectors/slack/scope.js.map +1 -0
- package/dist/src/connectors/slack/signature.js +20 -0
- package/dist/src/connectors/slack/signature.js.map +1 -0
- package/dist/src/connectors/slack/tenant-routing.js +17 -0
- package/dist/src/connectors/slack/tenant-routing.js.map +1 -0
- package/dist/src/connectors/slack/transform.js +31 -0
- package/dist/src/connectors/slack/transform.js.map +1 -0
- package/dist/src/connectors/slack/types.js +23 -0
- package/dist/src/connectors/slack/types.js.map +1 -0
- package/dist/src/connectors/slack/web-client.js +43 -0
- package/dist/src/connectors/slack/web-client.js.map +1 -0
- package/dist/src/consolidate.js +517 -0
- package/dist/src/consolidate.js.map +1 -0
- package/dist/src/dag.js +104 -0
- package/dist/src/dag.js.map +1 -0
- package/dist/src/dashboard.js +409 -0
- package/dist/src/dashboard.js.map +1 -0
- package/dist/src/db.js +584 -0
- package/dist/src/db.js.map +1 -0
- package/dist/src/embeddings.js +344 -0
- package/dist/src/embeddings.js.map +1 -0
- package/dist/src/eval-suite.js +289 -0
- package/dist/src/eval-suite.js.map +1 -0
- package/dist/src/eval.js +187 -0
- package/dist/src/eval.js.map +1 -0
- package/dist/src/extract.js +87 -0
- package/dist/src/extract.js.map +1 -0
- package/dist/src/handoff.js +30 -0
- package/dist/src/handoff.js.map +1 -0
- package/dist/src/hooks.js +582 -0
- package/dist/src/hooks.js.map +1 -0
- package/dist/src/importers.js +399 -0
- package/dist/src/importers.js.map +1 -0
- package/dist/src/index.js +25 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/invalidation.js +94 -0
- package/dist/src/invalidation.js.map +1 -0
- package/dist/src/mcp/framing.js +45 -0
- package/dist/src/mcp/framing.js.map +1 -0
- package/dist/src/mcp/server.js +510 -0
- package/dist/src/mcp/server.js.map +1 -0
- package/dist/src/memory.js +280 -0
- package/dist/src/memory.js.map +1 -0
- package/dist/src/multihop.js +32 -0
- package/dist/src/multihop.js.map +1 -0
- package/dist/src/path-context.js +32 -0
- package/dist/src/path-context.js.map +1 -0
- package/dist/src/physics-config.js +26 -0
- package/dist/src/physics-config.js.map +1 -0
- package/dist/src/physics-state.js +163 -0
- package/dist/src/physics-state.js.map +1 -0
- package/dist/src/physics.js +361 -0
- package/dist/src/physics.js.map +1 -0
- package/dist/src/postinstall.js +68 -0
- package/dist/src/postinstall.js.map +1 -0
- package/dist/src/raw-archive.js +72 -0
- package/dist/src/raw-archive.js.map +1 -0
- package/dist/src/refine-llm.js +147 -0
- package/dist/src/refine-llm.js.map +1 -0
- package/dist/src/replay.js +117 -0
- package/dist/src/replay.js.map +1 -0
- package/dist/src/salience.js +74 -0
- package/dist/src/salience.js.map +1 -0
- package/dist/src/scheduler.js +67 -0
- package/dist/src/scheduler.js.map +1 -0
- package/dist/src/scope.js +35 -0
- package/dist/src/scope.js.map +1 -0
- package/dist/src/search.js +801 -0
- package/dist/src/search.js.map +1 -0
- package/dist/src/server-detect.js +70 -0
- package/dist/src/server-detect.js.map +1 -0
- package/dist/src/server.js +784 -0
- package/dist/src/server.js.map +1 -0
- package/dist/src/shared.js +309 -0
- package/dist/src/shared.js.map +1 -0
- package/dist/src/sso.js +22 -0
- package/dist/src/sso.js.map +1 -0
- package/dist/src/store.js +1390 -0
- package/dist/src/store.js.map +1 -0
- package/dist/src/tenant.js +17 -0
- package/dist/src/tenant.js.map +1 -0
- package/dist/src/trace.js +64 -0
- package/dist/src/trace.js.map +1 -0
- package/dist/src/working-memory.js +149 -0
- package/dist/src/working-memory.js.map +1 -0
- package/dist/src/yaml.js +98 -0
- package/dist/src/yaml.js.map +1 -0
- package/dist/store.d.ts +25 -4
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +50 -11
- 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 +2 -2
- package/dist/import.d.ts +0 -31
- package/dist/import.d.ts.map +0 -1
- package/dist/import.js +0 -307
- package/dist/import.js.map +0 -1
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* E1.3 1000-event Slack smoke benchmark.
|
|
3
|
+
*
|
|
4
|
+
* ROADMAP success criterion: ingest 1000 synthetic Slack events, replay each
|
|
5
|
+
* one twice, and prove (a) exactly `count` unique memories are written
|
|
6
|
+
* (idempotency holds), (b) zero outbound HTTP calls escape the connector
|
|
7
|
+
* layer, (c) per-call latency stays under 500ms.
|
|
8
|
+
*
|
|
9
|
+
* The no-outbound-HTTP invariant is proven (review patch #5) by replacing
|
|
10
|
+
* `globalThis.fetch` with a function that THROWS on any call. If a future
|
|
11
|
+
* code path inside the connector ever reaches for fetch (e.g. a user
|
|
12
|
+
* resolution shortcut), this smoke fails loud rather than silently lying
|
|
13
|
+
* with a hardcoded 0. The original fetch is restored in `finally` so the
|
|
14
|
+
* spy never poisons later tests.
|
|
15
|
+
*/
|
|
16
|
+
import { ingestMessage } from '../../src/connectors/slack/ingest.js';
|
|
17
|
+
import { loadAllEntries } from '../../src/store.js';
|
|
18
|
+
export async function runSlackSmoke(opts) {
|
|
19
|
+
const ctx = {
|
|
20
|
+
hippoRoot: opts.hippoRoot,
|
|
21
|
+
tenantId: 'default',
|
|
22
|
+
actor: 'connector:slack',
|
|
23
|
+
};
|
|
24
|
+
const start = Date.now();
|
|
25
|
+
let calls = 0;
|
|
26
|
+
// Review patch #5: spy globalThis.fetch with a throwing function. The smoke
|
|
27
|
+
// fails LOUD if anything in the ingest path ever calls fetch().
|
|
28
|
+
const origFetch = globalThis.fetch;
|
|
29
|
+
let outboundHttp = 0;
|
|
30
|
+
globalThis.fetch = ((..._args) => {
|
|
31
|
+
outboundHttp++;
|
|
32
|
+
throw new Error('outbound HTTP forbidden during slack smoke');
|
|
33
|
+
});
|
|
34
|
+
try {
|
|
35
|
+
for (let pass = 0; pass < opts.replay; pass++) {
|
|
36
|
+
for (let i = 0; i < opts.count; i++) {
|
|
37
|
+
ingestMessage(ctx, {
|
|
38
|
+
teamId: 'T1',
|
|
39
|
+
channel: { id: 'C1', is_private: false },
|
|
40
|
+
message: {
|
|
41
|
+
type: 'message',
|
|
42
|
+
channel: 'C1',
|
|
43
|
+
user: 'U1',
|
|
44
|
+
// Use a content body well over the 3-char minimum enforced by
|
|
45
|
+
// memory.ts so synthetic events survive validation.
|
|
46
|
+
text: `slack synthetic event ${i}`,
|
|
47
|
+
ts: `${1700000000 + i}.000100`,
|
|
48
|
+
},
|
|
49
|
+
eventId: `Ev-${i}`,
|
|
50
|
+
});
|
|
51
|
+
calls++;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
finally {
|
|
56
|
+
globalThis.fetch = origFetch;
|
|
57
|
+
}
|
|
58
|
+
const uniqueMemories = loadAllEntries(opts.hippoRoot).filter((e) => e.tags.includes('source:slack')).length;
|
|
59
|
+
return {
|
|
60
|
+
uniqueMemories,
|
|
61
|
+
totalIngestCalls: calls,
|
|
62
|
+
outboundHttp,
|
|
63
|
+
durationMs: Date.now() - start,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
// Standalone CLI: 1000 events, replay=2, fail loud on regression.
|
|
67
|
+
// Use pathToFileURL for cross-platform `import.meta.url` matching (Windows
|
|
68
|
+
// emits `file:///C:/...` which `file://${process.argv[1]}` cannot match).
|
|
69
|
+
const { pathToFileURL } = await import('url');
|
|
70
|
+
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
|
|
71
|
+
const { mkdtempSync, rmSync } = await import('fs');
|
|
72
|
+
const { tmpdir } = await import('os');
|
|
73
|
+
const { join } = await import('path');
|
|
74
|
+
const { initStore } = await import('../../src/store.js');
|
|
75
|
+
const root = mkdtempSync(join(tmpdir(), 'hippo-slack-smoke-cli-'));
|
|
76
|
+
try {
|
|
77
|
+
initStore(root);
|
|
78
|
+
const r = await runSlackSmoke({ hippoRoot: root, count: 1000, replay: 2 });
|
|
79
|
+
console.log(JSON.stringify(r, null, 2));
|
|
80
|
+
if (r.uniqueMemories !== 1000) {
|
|
81
|
+
console.error('FAIL: uniqueMemories !== 1000');
|
|
82
|
+
process.exit(1);
|
|
83
|
+
}
|
|
84
|
+
if (r.outboundHttp !== 0) {
|
|
85
|
+
console.error('FAIL: outboundHttp !== 0');
|
|
86
|
+
process.exit(1);
|
|
87
|
+
}
|
|
88
|
+
if (r.totalIngestCalls !== 2000) {
|
|
89
|
+
console.error('FAIL: totalIngestCalls !== 2000');
|
|
90
|
+
process.exit(1);
|
|
91
|
+
}
|
|
92
|
+
if (r.durationMs / r.totalIngestCalls > 500) {
|
|
93
|
+
console.error('FAIL: per-call latency >500ms');
|
|
94
|
+
process.exit(1);
|
|
95
|
+
}
|
|
96
|
+
console.log('PASS');
|
|
97
|
+
}
|
|
98
|
+
finally {
|
|
99
|
+
rmSync(root, { recursive: true, force: true });
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=slack-1000-event-smoke.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slack-1000-event-smoke.js","sourceRoot":"","sources":["../../../benchmarks/e1.3/slack-1000-event-smoke.ts"],"names":[],"mappings":"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"}
|
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';
|
|
@@ -59,6 +62,9 @@ import { wmPush, wmRead, wmClear, wmFlush } from './working-memory.js';
|
|
|
59
62
|
import { multihopSearch } from './multihop.js';
|
|
60
63
|
import { computeSalience } from './salience.js';
|
|
61
64
|
import { computeAmbientState, renderAmbientSummary } from './ambient.js';
|
|
65
|
+
import { listDlq } from './connectors/slack/dlq.js';
|
|
66
|
+
import { backfillChannel } from './connectors/slack/backfill.js';
|
|
67
|
+
import { slackHistoryFetcher } from './connectors/slack/web-client.js';
|
|
62
68
|
// ---------------------------------------------------------------------------
|
|
63
69
|
// Helpers
|
|
64
70
|
// ---------------------------------------------------------------------------
|
|
@@ -99,6 +105,35 @@ function requireInit(hippoRoot) {
|
|
|
99
105
|
process.exit(1);
|
|
100
106
|
}
|
|
101
107
|
}
|
|
108
|
+
/**
|
|
109
|
+
* Run an HTTP-routed command if a `hippo serve` instance is detected for
|
|
110
|
+
* `hippoRoot`. Returns:
|
|
111
|
+
* - true if the HTTP path ran (success OR a structured server error that
|
|
112
|
+
* was already surfaced to stdout/stderr by `httpFn`),
|
|
113
|
+
* - false if no server was detected, or if the detected pidfile turned out
|
|
114
|
+
* to be stale (connection refused). On stale, the pidfile is removed
|
|
115
|
+
* and the caller should fall back to the direct path.
|
|
116
|
+
*
|
|
117
|
+
* Per the A1 plan footgun #1: stale pidfiles must self-heal, not crash.
|
|
118
|
+
*/
|
|
119
|
+
async function runViaServerIfAvailable(hippoRoot, httpFn) {
|
|
120
|
+
const info = detectServer(hippoRoot);
|
|
121
|
+
if (!info)
|
|
122
|
+
return false;
|
|
123
|
+
const apiKey = process.env['HIPPO_API_KEY'];
|
|
124
|
+
try {
|
|
125
|
+
await httpFn(info, apiKey);
|
|
126
|
+
return true;
|
|
127
|
+
}
|
|
128
|
+
catch (err) {
|
|
129
|
+
if (client.isConnectionRefused(err)) {
|
|
130
|
+
console.error('hippo: stale server pidfile detected, falling back to direct mode');
|
|
131
|
+
removePidfile(hippoRoot);
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
throw err;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
102
137
|
function parseArgs(argv) {
|
|
103
138
|
const [, , command = '', ...rest] = argv;
|
|
104
139
|
const args = [];
|
|
@@ -2159,12 +2194,17 @@ function cmdOutcome(hippoRoot, flags) {
|
|
|
2159
2194
|
}
|
|
2160
2195
|
function cmdForget(hippoRoot, id) {
|
|
2161
2196
|
requireInit(hippoRoot);
|
|
2162
|
-
const
|
|
2163
|
-
|
|
2197
|
+
const ctx = {
|
|
2198
|
+
hippoRoot,
|
|
2199
|
+
tenantId: resolveTenantId({}),
|
|
2200
|
+
actor: 'cli',
|
|
2201
|
+
};
|
|
2202
|
+
try {
|
|
2203
|
+
api.forget(ctx, id);
|
|
2164
2204
|
updateStats(hippoRoot, { forgotten: 1 });
|
|
2165
2205
|
console.log(`Forgot ${id}`);
|
|
2166
2206
|
}
|
|
2167
|
-
|
|
2207
|
+
catch {
|
|
2168
2208
|
console.error(`Memory not found: ${id}`);
|
|
2169
2209
|
process.exit(1);
|
|
2170
2210
|
}
|
|
@@ -3238,14 +3278,14 @@ function cmdPromote(hippoRoot, id) {
|
|
|
3238
3278
|
console.error('Usage: hippo promote <id>');
|
|
3239
3279
|
process.exit(1);
|
|
3240
3280
|
}
|
|
3281
|
+
const ctx = {
|
|
3282
|
+
hippoRoot,
|
|
3283
|
+
tenantId: resolveTenantId({}),
|
|
3284
|
+
actor: 'cli',
|
|
3285
|
+
};
|
|
3241
3286
|
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}`);
|
|
3287
|
+
const result = api.promote(ctx, id);
|
|
3288
|
+
console.log(`Promoted ${id} to global store as ${result.globalId}`);
|
|
3249
3289
|
console.log(` Global store: ${getGlobalRoot()}`);
|
|
3250
3290
|
}
|
|
3251
3291
|
catch (err) {
|
|
@@ -3813,21 +3853,18 @@ function cmdAuthCreate(hippoRoot, flags) {
|
|
|
3813
3853
|
const root = resolveAuthRoot(hippoRoot, flags);
|
|
3814
3854
|
const tenantFlag = typeof flags['tenant'] === 'string' ? flags['tenant'] : undefined;
|
|
3815
3855
|
const labelFlag = typeof flags['label'] === 'string' ? flags['label'] : undefined;
|
|
3816
|
-
const tenantId = tenantFlag ?? resolveTenantId({});
|
|
3817
3856
|
const asJson = Boolean(flags['json']);
|
|
3818
|
-
const
|
|
3819
|
-
|
|
3820
|
-
|
|
3821
|
-
|
|
3822
|
-
}
|
|
3823
|
-
|
|
3824
|
-
closeHippoDb(db);
|
|
3825
|
-
}
|
|
3857
|
+
const ctx = {
|
|
3858
|
+
hippoRoot: root,
|
|
3859
|
+
tenantId: resolveTenantId({}),
|
|
3860
|
+
actor: 'cli',
|
|
3861
|
+
};
|
|
3862
|
+
const result = api.authCreate(ctx, { tenantId: tenantFlag, label: labelFlag });
|
|
3826
3863
|
if (asJson) {
|
|
3827
3864
|
console.log(JSON.stringify({
|
|
3828
3865
|
keyId: result.keyId,
|
|
3829
3866
|
plaintext: result.plaintext,
|
|
3830
|
-
tenantId,
|
|
3867
|
+
tenantId: result.tenantId,
|
|
3831
3868
|
label: labelFlag ?? null,
|
|
3832
3869
|
}));
|
|
3833
3870
|
return;
|
|
@@ -3969,14 +4006,8 @@ function cmdAuditList(hippoRoot, flags) {
|
|
|
3969
4006
|
console.error(`--limit must be between 1 and 10000 (got ${limit}).`);
|
|
3970
4007
|
process.exit(1);
|
|
3971
4008
|
}
|
|
3972
|
-
const
|
|
3973
|
-
|
|
3974
|
-
try {
|
|
3975
|
-
events = queryAuditEvents(db, { tenantId, op, since, limit });
|
|
3976
|
-
}
|
|
3977
|
-
finally {
|
|
3978
|
-
closeHippoDb(db);
|
|
3979
|
-
}
|
|
4009
|
+
const ctx = { hippoRoot: root, tenantId, actor: 'cli' };
|
|
4010
|
+
const events = api.auditList(ctx, { op, since, limit });
|
|
3980
4011
|
if (asJson) {
|
|
3981
4012
|
console.log(JSON.stringify(events));
|
|
3982
4013
|
return;
|
|
@@ -4027,6 +4058,82 @@ function cmdAuth(hippoRoot, args, flags) {
|
|
|
4027
4058
|
process.exit(1);
|
|
4028
4059
|
}
|
|
4029
4060
|
}
|
|
4061
|
+
// ---------------------------------------------------------------------------
|
|
4062
|
+
// Slack subcommands (E1.3 — `hippo slack backfill` / `hippo slack dlq list`)
|
|
4063
|
+
// ---------------------------------------------------------------------------
|
|
4064
|
+
function printSlackBackfillUsage() {
|
|
4065
|
+
console.log('hippo slack backfill --channel <id> [--since ISO]');
|
|
4066
|
+
console.log(' --channel Slack channel id (required, e.g. C0123ABC)');
|
|
4067
|
+
console.log(' --since backfill from ISO timestamp (default: cursor)');
|
|
4068
|
+
}
|
|
4069
|
+
function cmdSlackBackfill(hippoRoot, flags) {
|
|
4070
|
+
// M3: detect --help BEFORE token check so operators can read usage in
|
|
4071
|
+
// environments without SLACK_BOT_TOKEN configured.
|
|
4072
|
+
if (flags['help']) {
|
|
4073
|
+
printSlackBackfillUsage();
|
|
4074
|
+
return;
|
|
4075
|
+
}
|
|
4076
|
+
const channel = typeof flags['channel'] === 'string' ? flags['channel'] : undefined;
|
|
4077
|
+
if (!channel) {
|
|
4078
|
+
printSlackBackfillUsage();
|
|
4079
|
+
process.exit(1);
|
|
4080
|
+
}
|
|
4081
|
+
// Real fetcher requires SLACK_BOT_TOKEN with channels:history scope.
|
|
4082
|
+
const token = process.env.SLACK_BOT_TOKEN;
|
|
4083
|
+
if (!token) {
|
|
4084
|
+
console.error('SLACK_BOT_TOKEN is not set. Backfill requires a Slack bot token with channels:history scope.');
|
|
4085
|
+
process.exit(2);
|
|
4086
|
+
}
|
|
4087
|
+
// --since is advisory in V1: the slack_cursors row drives resume, so the
|
|
4088
|
+
// backfill loop always picks up where it last left off. Honoured-by-cursor
|
|
4089
|
+
// semantics keep idempotency clean.
|
|
4090
|
+
const sinceIso = flags['since'];
|
|
4091
|
+
void sinceIso;
|
|
4092
|
+
const fetcher = slackHistoryFetcher(token);
|
|
4093
|
+
const ctx = {
|
|
4094
|
+
hippoRoot,
|
|
4095
|
+
tenantId: process.env.HIPPO_TENANT ?? 'default',
|
|
4096
|
+
actor: 'cli:slack-backfill',
|
|
4097
|
+
};
|
|
4098
|
+
backfillChannel(ctx, {
|
|
4099
|
+
teamId: process.env.SLACK_TEAM_ID ?? 'T_UNKNOWN',
|
|
4100
|
+
channel: { id: channel, is_private: false },
|
|
4101
|
+
fetcher,
|
|
4102
|
+
})
|
|
4103
|
+
.then((r) => {
|
|
4104
|
+
console.log(`backfill ${channel}: ${r.ingested} new messages across ${r.pages} pages`);
|
|
4105
|
+
})
|
|
4106
|
+
.catch((e) => {
|
|
4107
|
+
console.error('backfill failed:', e.message);
|
|
4108
|
+
process.exit(3);
|
|
4109
|
+
});
|
|
4110
|
+
}
|
|
4111
|
+
function cmdSlackDlqList(hippoRoot, _flags) {
|
|
4112
|
+
const db = openHippoDb(hippoRoot);
|
|
4113
|
+
try {
|
|
4114
|
+
const tenantId = process.env.HIPPO_TENANT ?? 'default';
|
|
4115
|
+
const items = listDlq(db, { tenantId });
|
|
4116
|
+
for (const it of items) {
|
|
4117
|
+
console.log(`${it.id}\t${it.receivedAt}\t${it.error}`);
|
|
4118
|
+
}
|
|
4119
|
+
}
|
|
4120
|
+
finally {
|
|
4121
|
+
closeHippoDb(db);
|
|
4122
|
+
}
|
|
4123
|
+
}
|
|
4124
|
+
function cmdSlack(hippoRoot, args, flags) {
|
|
4125
|
+
const sub = args[0];
|
|
4126
|
+
if (sub === 'backfill') {
|
|
4127
|
+
cmdSlackBackfill(hippoRoot, flags);
|
|
4128
|
+
return;
|
|
4129
|
+
}
|
|
4130
|
+
if (sub === 'dlq' && args[1] === 'list') {
|
|
4131
|
+
cmdSlackDlqList(hippoRoot, flags);
|
|
4132
|
+
return;
|
|
4133
|
+
}
|
|
4134
|
+
console.error('Usage: hippo slack <backfill|dlq list> [...]');
|
|
4135
|
+
process.exit(1);
|
|
4136
|
+
}
|
|
4030
4137
|
function printUsage() {
|
|
4031
4138
|
console.log(`
|
|
4032
4139
|
Hippo - biologically-inspired memory system for AI agents
|
|
@@ -4350,6 +4457,37 @@ async function main() {
|
|
|
4350
4457
|
console.error('Memory content too short (minimum 3 characters).');
|
|
4351
4458
|
process.exit(1);
|
|
4352
4459
|
}
|
|
4460
|
+
// Thin-client routing. When a server is up, simple `remember` calls go
|
|
4461
|
+
// over HTTP so the daemon stays single-writer (footgun #2). Rich CLI
|
|
4462
|
+
// flags (--pin, --layer, --extract, --global, salience gates) still
|
|
4463
|
+
// need the direct path; we only intercept the minimal envelope.
|
|
4464
|
+
const richFlag = flags['pin'] || flags['global'] || flags['extract'] || flags['force'] ||
|
|
4465
|
+
flags['observed'] || flags['inferred'] || flags['verified'] ||
|
|
4466
|
+
flags['layer'] !== undefined;
|
|
4467
|
+
if (!richFlag) {
|
|
4468
|
+
const rememberKindRaw = typeof flags['kind'] === 'string' ? flags['kind'].toLowerCase() : undefined;
|
|
4469
|
+
const rememberKindAllowed = ['distilled', 'superseded'];
|
|
4470
|
+
if (rememberKindRaw === undefined || rememberKindAllowed.includes(rememberKindRaw)) {
|
|
4471
|
+
const tagsRaw = flags['tag'];
|
|
4472
|
+
const tags = Array.isArray(tagsRaw)
|
|
4473
|
+
? tagsRaw.map(String)
|
|
4474
|
+
: typeof tagsRaw === 'string' ? [tagsRaw] : undefined;
|
|
4475
|
+
const remembered = await runViaServerIfAvailable(hippoRoot, async (info, apiKey) => {
|
|
4476
|
+
const result = await client.remember(info.url, apiKey, {
|
|
4477
|
+
content: text,
|
|
4478
|
+
kind: rememberKindRaw,
|
|
4479
|
+
scope: typeof flags['scope'] === 'string' ? flags['scope'] : undefined,
|
|
4480
|
+
owner: typeof flags['owner'] === 'string' ? flags['owner'] : undefined,
|
|
4481
|
+
artifactRef: typeof flags['artifact-ref'] === 'string' ? flags['artifact-ref'] : undefined,
|
|
4482
|
+
tags,
|
|
4483
|
+
});
|
|
4484
|
+
console.log(`Remembered [${result.id}] (via ${info.url})`);
|
|
4485
|
+
console.log(` Kind: ${result.kind} | Tenant: ${result.tenantId}`);
|
|
4486
|
+
});
|
|
4487
|
+
if (remembered)
|
|
4488
|
+
break;
|
|
4489
|
+
}
|
|
4490
|
+
}
|
|
4353
4491
|
await cmdRemember(hippoRoot, text, flags);
|
|
4354
4492
|
break;
|
|
4355
4493
|
}
|
|
@@ -4429,6 +4567,9 @@ async function main() {
|
|
|
4429
4567
|
case 'auth':
|
|
4430
4568
|
cmdAuth(hippoRoot, args, flags);
|
|
4431
4569
|
break;
|
|
4570
|
+
case 'slack':
|
|
4571
|
+
cmdSlack(hippoRoot, args, flags);
|
|
4572
|
+
break;
|
|
4432
4573
|
case 'audit': {
|
|
4433
4574
|
// `audit list` -> A5 audit-log viewer. Other forms (no sub, --fix) keep
|
|
4434
4575
|
// the existing memory-quality auditor for backwards compatibility.
|
|
@@ -4499,6 +4640,18 @@ async function main() {
|
|
|
4499
4640
|
console.error('Please provide a memory ID.');
|
|
4500
4641
|
process.exit(1);
|
|
4501
4642
|
}
|
|
4643
|
+
const routed = await runViaServerIfAvailable(hippoRoot, async (info, apiKey) => {
|
|
4644
|
+
try {
|
|
4645
|
+
await client.forget(info.url, apiKey, id);
|
|
4646
|
+
console.log(`Forgot ${id}`);
|
|
4647
|
+
}
|
|
4648
|
+
catch (err) {
|
|
4649
|
+
console.error(err.message);
|
|
4650
|
+
process.exit(1);
|
|
4651
|
+
}
|
|
4652
|
+
});
|
|
4653
|
+
if (routed)
|
|
4654
|
+
break;
|
|
4502
4655
|
cmdForget(hippoRoot, id);
|
|
4503
4656
|
break;
|
|
4504
4657
|
}
|
|
@@ -4540,6 +4693,18 @@ async function main() {
|
|
|
4540
4693
|
console.error('Please provide a memory ID.');
|
|
4541
4694
|
process.exit(1);
|
|
4542
4695
|
}
|
|
4696
|
+
const promoted = await runViaServerIfAvailable(hippoRoot, async (info, apiKey) => {
|
|
4697
|
+
try {
|
|
4698
|
+
const result = await client.promote(info.url, apiKey, id);
|
|
4699
|
+
console.log(`Promoted ${id} to global store as ${result.globalId}`);
|
|
4700
|
+
}
|
|
4701
|
+
catch (err) {
|
|
4702
|
+
console.error(`Failed to promote: ${err.message}`);
|
|
4703
|
+
process.exit(1);
|
|
4704
|
+
}
|
|
4705
|
+
});
|
|
4706
|
+
if (promoted)
|
|
4707
|
+
break;
|
|
4543
4708
|
cmdPromote(hippoRoot, id);
|
|
4544
4709
|
break;
|
|
4545
4710
|
}
|
|
@@ -4689,12 +4854,35 @@ async function main() {
|
|
|
4689
4854
|
case 'wm':
|
|
4690
4855
|
cmdWm(hippoRoot, args, flags);
|
|
4691
4856
|
break;
|
|
4692
|
-
case 'mcp':
|
|
4693
|
-
// Start MCP server over stdio
|
|
4694
|
-
|
|
4857
|
+
case 'mcp': {
|
|
4858
|
+
// Start MCP server over stdio. Dynamic import keeps main CLI lean; the
|
|
4859
|
+
// dispatcher itself is transport-agnostic, so we explicitly attach the
|
|
4860
|
+
// stdio loop here. (HTTP/SSE transport is wired in src/server.ts and
|
|
4861
|
+
// imports the same module without triggering stdin handlers.)
|
|
4862
|
+
const mod = await import('./mcp/server.js');
|
|
4863
|
+
mod.startStdioLoop();
|
|
4695
4864
|
// Server runs until stdin closes, so we never reach here
|
|
4696
4865
|
await new Promise(() => { }); // hang forever
|
|
4697
4866
|
break;
|
|
4867
|
+
}
|
|
4868
|
+
case 'serve': {
|
|
4869
|
+
requireInit(hippoRoot);
|
|
4870
|
+
const portRaw = flags['port'] ?? process.env['HIPPO_PORT'] ?? '6789';
|
|
4871
|
+
const port = Number(portRaw);
|
|
4872
|
+
if (!Number.isFinite(port) || port < 0) {
|
|
4873
|
+
console.error(`Invalid --port: ${String(portRaw)}`);
|
|
4874
|
+
process.exit(1);
|
|
4875
|
+
}
|
|
4876
|
+
const host = typeof flags['host'] === 'string' ? flags['host'] : '127.0.0.1';
|
|
4877
|
+
const { serve } = await import('./server.js');
|
|
4878
|
+
const handle = await serve({ hippoRoot, port, host });
|
|
4879
|
+
console.log(`hippo serve listening on ${handle.url} (pid ${process.pid})`);
|
|
4880
|
+
console.log(`pidfile: ${path.join(hippoRoot, 'server.pid')}`);
|
|
4881
|
+
console.log('press Ctrl+C to stop');
|
|
4882
|
+
// SIGINT/SIGTERM handlers wired in server.ts (skipped under VITEST). Hang.
|
|
4883
|
+
await new Promise(() => { });
|
|
4884
|
+
break;
|
|
4885
|
+
}
|
|
4698
4886
|
case 'invalidate': {
|
|
4699
4887
|
requireInit(hippoRoot);
|
|
4700
4888
|
const target = args[0];
|