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.
Files changed (215) hide show
  1. package/README.md +25 -0
  2. package/dist/api.d.ts +183 -0
  3. package/dist/api.d.ts.map +1 -0
  4. package/dist/api.js +343 -0
  5. package/dist/api.js.map +1 -0
  6. package/dist/benchmarks/e1.3/incident-recall-eval.js +74 -0
  7. package/dist/benchmarks/e1.3/incident-recall-eval.js.map +1 -0
  8. package/dist/benchmarks/e1.3/scenarios.json +2587 -0
  9. package/dist/benchmarks/e1.3/slack-1000-event-smoke.js +102 -0
  10. package/dist/benchmarks/e1.3/slack-1000-event-smoke.js.map +1 -0
  11. package/dist/cli.js +222 -34
  12. package/dist/cli.js.map +1 -1
  13. package/dist/client.d.ts +54 -0
  14. package/dist/client.d.ts.map +1 -0
  15. package/dist/client.js +181 -0
  16. package/dist/client.js.map +1 -0
  17. package/dist/connectors/slack/backfill.d.ts +42 -0
  18. package/dist/connectors/slack/backfill.d.ts.map +1 -0
  19. package/dist/connectors/slack/backfill.js +76 -0
  20. package/dist/connectors/slack/backfill.js.map +1 -0
  21. package/dist/connectors/slack/deletion.d.ts +14 -0
  22. package/dist/connectors/slack/deletion.d.ts.map +1 -0
  23. package/dist/connectors/slack/deletion.js +46 -0
  24. package/dist/connectors/slack/deletion.js.map +1 -0
  25. package/dist/connectors/slack/dlq.d.ts +21 -0
  26. package/dist/connectors/slack/dlq.d.ts.map +1 -0
  27. package/dist/connectors/slack/dlq.js +23 -0
  28. package/dist/connectors/slack/dlq.js.map +1 -0
  29. package/dist/connectors/slack/idempotency.d.ts +5 -0
  30. package/dist/connectors/slack/idempotency.d.ts.map +1 -0
  31. package/dist/connectors/slack/idempotency.js +13 -0
  32. package/dist/connectors/slack/idempotency.js.map +1 -0
  33. package/dist/connectors/slack/ingest.d.ts +27 -0
  34. package/dist/connectors/slack/ingest.d.ts.map +1 -0
  35. package/dist/connectors/slack/ingest.js +48 -0
  36. package/dist/connectors/slack/ingest.js.map +1 -0
  37. package/dist/connectors/slack/ratelimit.d.ts +9 -0
  38. package/dist/connectors/slack/ratelimit.d.ts.map +1 -0
  39. package/dist/connectors/slack/ratelimit.js +18 -0
  40. package/dist/connectors/slack/ratelimit.js.map +1 -0
  41. package/dist/connectors/slack/scope.d.ts +16 -0
  42. package/dist/connectors/slack/scope.d.ts.map +1 -0
  43. package/dist/connectors/slack/scope.js +13 -0
  44. package/dist/connectors/slack/scope.js.map +1 -0
  45. package/dist/connectors/slack/signature.d.ts +12 -0
  46. package/dist/connectors/slack/signature.d.ts.map +1 -0
  47. package/dist/connectors/slack/signature.js +20 -0
  48. package/dist/connectors/slack/signature.js.map +1 -0
  49. package/dist/connectors/slack/tenant-routing.d.ts +13 -0
  50. package/dist/connectors/slack/tenant-routing.d.ts.map +1 -0
  51. package/dist/connectors/slack/tenant-routing.js +17 -0
  52. package/dist/connectors/slack/tenant-routing.js.map +1 -0
  53. package/dist/connectors/slack/transform.d.ts +20 -0
  54. package/dist/connectors/slack/transform.d.ts.map +1 -0
  55. package/dist/connectors/slack/transform.js +31 -0
  56. package/dist/connectors/slack/transform.js.map +1 -0
  57. package/dist/connectors/slack/types.d.ts +35 -0
  58. package/dist/connectors/slack/types.d.ts.map +1 -0
  59. package/dist/connectors/slack/types.js +23 -0
  60. package/dist/connectors/slack/types.js.map +1 -0
  61. package/dist/connectors/slack/web-client.d.ts +12 -0
  62. package/dist/connectors/slack/web-client.d.ts.map +1 -0
  63. package/dist/connectors/slack/web-client.js +43 -0
  64. package/dist/connectors/slack/web-client.js.map +1 -0
  65. package/dist/db.d.ts.map +1 -1
  66. package/dist/db.js +46 -1
  67. package/dist/db.js.map +1 -1
  68. package/dist/importers.js +3 -3
  69. package/dist/importers.js.map +1 -1
  70. package/dist/mcp/server.d.ts +46 -1
  71. package/dist/mcp/server.d.ts.map +1 -1
  72. package/dist/mcp/server.js +74 -26
  73. package/dist/mcp/server.js.map +1 -1
  74. package/dist/server-detect.d.ts +26 -0
  75. package/dist/server-detect.d.ts.map +1 -0
  76. package/dist/server-detect.js +70 -0
  77. package/dist/server-detect.js.map +1 -0
  78. package/dist/server.d.ts +29 -0
  79. package/dist/server.d.ts.map +1 -0
  80. package/dist/server.js +784 -0
  81. package/dist/server.js.map +1 -0
  82. package/dist/shared.d.ts +3 -1
  83. package/dist/shared.d.ts.map +1 -1
  84. package/dist/shared.js +2 -2
  85. package/dist/shared.js.map +1 -1
  86. package/dist/src/ambient.js +147 -0
  87. package/dist/src/ambient.js.map +1 -0
  88. package/dist/src/api.js +343 -0
  89. package/dist/src/api.js.map +1 -0
  90. package/dist/src/audit.js +152 -0
  91. package/dist/src/audit.js.map +1 -0
  92. package/dist/src/auth.js +65 -0
  93. package/dist/src/auth.js.map +1 -0
  94. package/dist/src/autolearn.js +143 -0
  95. package/dist/src/autolearn.js.map +1 -0
  96. package/dist/src/capture.js +512 -0
  97. package/dist/src/capture.js.map +1 -0
  98. package/dist/src/cli.js +4971 -0
  99. package/dist/src/cli.js.map +1 -0
  100. package/dist/src/client.js +181 -0
  101. package/dist/src/client.js.map +1 -0
  102. package/dist/src/config.js +108 -0
  103. package/dist/src/config.js.map +1 -0
  104. package/dist/src/connectors/slack/backfill.js +76 -0
  105. package/dist/src/connectors/slack/backfill.js.map +1 -0
  106. package/dist/src/connectors/slack/deletion.js +46 -0
  107. package/dist/src/connectors/slack/deletion.js.map +1 -0
  108. package/dist/src/connectors/slack/dlq.js +23 -0
  109. package/dist/src/connectors/slack/dlq.js.map +1 -0
  110. package/dist/src/connectors/slack/idempotency.js +13 -0
  111. package/dist/src/connectors/slack/idempotency.js.map +1 -0
  112. package/dist/src/connectors/slack/ingest.js +48 -0
  113. package/dist/src/connectors/slack/ingest.js.map +1 -0
  114. package/dist/src/connectors/slack/ratelimit.js +18 -0
  115. package/dist/src/connectors/slack/ratelimit.js.map +1 -0
  116. package/dist/src/connectors/slack/scope.js +13 -0
  117. package/dist/src/connectors/slack/scope.js.map +1 -0
  118. package/dist/src/connectors/slack/signature.js +20 -0
  119. package/dist/src/connectors/slack/signature.js.map +1 -0
  120. package/dist/src/connectors/slack/tenant-routing.js +17 -0
  121. package/dist/src/connectors/slack/tenant-routing.js.map +1 -0
  122. package/dist/src/connectors/slack/transform.js +31 -0
  123. package/dist/src/connectors/slack/transform.js.map +1 -0
  124. package/dist/src/connectors/slack/types.js +23 -0
  125. package/dist/src/connectors/slack/types.js.map +1 -0
  126. package/dist/src/connectors/slack/web-client.js +43 -0
  127. package/dist/src/connectors/slack/web-client.js.map +1 -0
  128. package/dist/src/consolidate.js +517 -0
  129. package/dist/src/consolidate.js.map +1 -0
  130. package/dist/src/dag.js +104 -0
  131. package/dist/src/dag.js.map +1 -0
  132. package/dist/src/dashboard.js +409 -0
  133. package/dist/src/dashboard.js.map +1 -0
  134. package/dist/src/db.js +584 -0
  135. package/dist/src/db.js.map +1 -0
  136. package/dist/src/embeddings.js +344 -0
  137. package/dist/src/embeddings.js.map +1 -0
  138. package/dist/src/eval-suite.js +289 -0
  139. package/dist/src/eval-suite.js.map +1 -0
  140. package/dist/src/eval.js +187 -0
  141. package/dist/src/eval.js.map +1 -0
  142. package/dist/src/extract.js +87 -0
  143. package/dist/src/extract.js.map +1 -0
  144. package/dist/src/handoff.js +30 -0
  145. package/dist/src/handoff.js.map +1 -0
  146. package/dist/src/hooks.js +582 -0
  147. package/dist/src/hooks.js.map +1 -0
  148. package/dist/src/importers.js +399 -0
  149. package/dist/src/importers.js.map +1 -0
  150. package/dist/src/index.js +25 -0
  151. package/dist/src/index.js.map +1 -0
  152. package/dist/src/invalidation.js +94 -0
  153. package/dist/src/invalidation.js.map +1 -0
  154. package/dist/src/mcp/framing.js +45 -0
  155. package/dist/src/mcp/framing.js.map +1 -0
  156. package/dist/src/mcp/server.js +510 -0
  157. package/dist/src/mcp/server.js.map +1 -0
  158. package/dist/src/memory.js +280 -0
  159. package/dist/src/memory.js.map +1 -0
  160. package/dist/src/multihop.js +32 -0
  161. package/dist/src/multihop.js.map +1 -0
  162. package/dist/src/path-context.js +32 -0
  163. package/dist/src/path-context.js.map +1 -0
  164. package/dist/src/physics-config.js +26 -0
  165. package/dist/src/physics-config.js.map +1 -0
  166. package/dist/src/physics-state.js +163 -0
  167. package/dist/src/physics-state.js.map +1 -0
  168. package/dist/src/physics.js +361 -0
  169. package/dist/src/physics.js.map +1 -0
  170. package/dist/src/postinstall.js +68 -0
  171. package/dist/src/postinstall.js.map +1 -0
  172. package/dist/src/raw-archive.js +72 -0
  173. package/dist/src/raw-archive.js.map +1 -0
  174. package/dist/src/refine-llm.js +147 -0
  175. package/dist/src/refine-llm.js.map +1 -0
  176. package/dist/src/replay.js +117 -0
  177. package/dist/src/replay.js.map +1 -0
  178. package/dist/src/salience.js +74 -0
  179. package/dist/src/salience.js.map +1 -0
  180. package/dist/src/scheduler.js +67 -0
  181. package/dist/src/scheduler.js.map +1 -0
  182. package/dist/src/scope.js +35 -0
  183. package/dist/src/scope.js.map +1 -0
  184. package/dist/src/search.js +801 -0
  185. package/dist/src/search.js.map +1 -0
  186. package/dist/src/server-detect.js +70 -0
  187. package/dist/src/server-detect.js.map +1 -0
  188. package/dist/src/server.js +784 -0
  189. package/dist/src/server.js.map +1 -0
  190. package/dist/src/shared.js +309 -0
  191. package/dist/src/shared.js.map +1 -0
  192. package/dist/src/sso.js +22 -0
  193. package/dist/src/sso.js.map +1 -0
  194. package/dist/src/store.js +1390 -0
  195. package/dist/src/store.js.map +1 -0
  196. package/dist/src/tenant.js +17 -0
  197. package/dist/src/tenant.js.map +1 -0
  198. package/dist/src/trace.js +64 -0
  199. package/dist/src/trace.js.map +1 -0
  200. package/dist/src/working-memory.js +149 -0
  201. package/dist/src/working-memory.js.map +1 -0
  202. package/dist/src/yaml.js +98 -0
  203. package/dist/src/yaml.js.map +1 -0
  204. package/dist/store.d.ts +25 -4
  205. package/dist/store.d.ts.map +1 -1
  206. package/dist/store.js +50 -11
  207. package/dist/store.js.map +1 -1
  208. package/extensions/openclaw-plugin/openclaw.plugin.json +1 -1
  209. package/extensions/openclaw-plugin/package.json +1 -1
  210. package/openclaw.plugin.json +1 -1
  211. package/package.json +2 -2
  212. package/dist/import.d.ts +0 -31
  213. package/dist/import.d.ts.map +0 -1
  214. package/dist/import.js +0 -307
  215. 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, promoteToGlobal, shareMemory, listPeers, autoShare, transferScore, searchBothHybrid, syncGlobalToLocal, } from './shared.js';
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, queryAuditEvents, } from './audit.js';
53
- import { createApiKey, listApiKeys, revokeApiKey } from './auth.js';
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 ok = deleteEntry(hippoRoot, id);
2163
- if (ok) {
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
- else {
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 globalEntry = promoteToGlobal(hippoRoot, id);
3243
- // Emit audit on the global store (where the promoted memory now lives).
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 db = openHippoDb(root);
3819
- let result;
3820
- try {
3821
- result = createApiKey(db, { tenantId, label: labelFlag });
3822
- }
3823
- finally {
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 db = openHippoDb(root);
3973
- let events;
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 - dynamically import to keep main CLI lean
4694
- await import('./mcp/server.js');
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];