hippo-memory 0.36.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 (199) hide show
  1. package/README.md +16 -0
  2. package/dist/api.d.ts +20 -0
  3. package/dist/api.d.ts.map +1 -1
  4. package/dist/api.js +23 -3
  5. package/dist/api.js.map +1 -1
  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 +82 -0
  12. package/dist/cli.js.map +1 -1
  13. package/dist/connectors/slack/backfill.d.ts +42 -0
  14. package/dist/connectors/slack/backfill.d.ts.map +1 -0
  15. package/dist/connectors/slack/backfill.js +76 -0
  16. package/dist/connectors/slack/backfill.js.map +1 -0
  17. package/dist/connectors/slack/deletion.d.ts +14 -0
  18. package/dist/connectors/slack/deletion.d.ts.map +1 -0
  19. package/dist/connectors/slack/deletion.js +46 -0
  20. package/dist/connectors/slack/deletion.js.map +1 -0
  21. package/dist/connectors/slack/dlq.d.ts +21 -0
  22. package/dist/connectors/slack/dlq.d.ts.map +1 -0
  23. package/dist/connectors/slack/dlq.js +23 -0
  24. package/dist/connectors/slack/dlq.js.map +1 -0
  25. package/dist/connectors/slack/idempotency.d.ts +5 -0
  26. package/dist/connectors/slack/idempotency.d.ts.map +1 -0
  27. package/dist/connectors/slack/idempotency.js +13 -0
  28. package/dist/connectors/slack/idempotency.js.map +1 -0
  29. package/dist/connectors/slack/ingest.d.ts +27 -0
  30. package/dist/connectors/slack/ingest.d.ts.map +1 -0
  31. package/dist/connectors/slack/ingest.js +48 -0
  32. package/dist/connectors/slack/ingest.js.map +1 -0
  33. package/dist/connectors/slack/ratelimit.d.ts +9 -0
  34. package/dist/connectors/slack/ratelimit.d.ts.map +1 -0
  35. package/dist/connectors/slack/ratelimit.js +18 -0
  36. package/dist/connectors/slack/ratelimit.js.map +1 -0
  37. package/dist/connectors/slack/scope.d.ts +16 -0
  38. package/dist/connectors/slack/scope.d.ts.map +1 -0
  39. package/dist/connectors/slack/scope.js +13 -0
  40. package/dist/connectors/slack/scope.js.map +1 -0
  41. package/dist/connectors/slack/signature.d.ts +12 -0
  42. package/dist/connectors/slack/signature.d.ts.map +1 -0
  43. package/dist/connectors/slack/signature.js +20 -0
  44. package/dist/connectors/slack/signature.js.map +1 -0
  45. package/dist/connectors/slack/tenant-routing.d.ts +13 -0
  46. package/dist/connectors/slack/tenant-routing.d.ts.map +1 -0
  47. package/dist/connectors/slack/tenant-routing.js +17 -0
  48. package/dist/connectors/slack/tenant-routing.js.map +1 -0
  49. package/dist/connectors/slack/transform.d.ts +20 -0
  50. package/dist/connectors/slack/transform.d.ts.map +1 -0
  51. package/dist/connectors/slack/transform.js +31 -0
  52. package/dist/connectors/slack/transform.js.map +1 -0
  53. package/dist/connectors/slack/types.d.ts +35 -0
  54. package/dist/connectors/slack/types.d.ts.map +1 -0
  55. package/dist/connectors/slack/types.js +23 -0
  56. package/dist/connectors/slack/types.js.map +1 -0
  57. package/dist/connectors/slack/web-client.d.ts +12 -0
  58. package/dist/connectors/slack/web-client.d.ts.map +1 -0
  59. package/dist/connectors/slack/web-client.js +43 -0
  60. package/dist/connectors/slack/web-client.js.map +1 -0
  61. package/dist/db.d.ts.map +1 -1
  62. package/dist/db.js +46 -1
  63. package/dist/db.js.map +1 -1
  64. package/dist/importers.js +3 -3
  65. package/dist/importers.js.map +1 -1
  66. package/dist/mcp/server.js +1 -1
  67. package/dist/server.d.ts.map +1 -1
  68. package/dist/server.js +174 -2
  69. package/dist/server.js.map +1 -1
  70. package/dist/src/ambient.js +147 -0
  71. package/dist/src/ambient.js.map +1 -0
  72. package/dist/src/api.js +343 -0
  73. package/dist/src/api.js.map +1 -0
  74. package/dist/src/audit.js +152 -0
  75. package/dist/src/audit.js.map +1 -0
  76. package/dist/src/auth.js +65 -0
  77. package/dist/src/auth.js.map +1 -0
  78. package/dist/src/autolearn.js +143 -0
  79. package/dist/src/autolearn.js.map +1 -0
  80. package/dist/src/capture.js +512 -0
  81. package/dist/src/capture.js.map +1 -0
  82. package/dist/src/cli.js +4971 -0
  83. package/dist/src/cli.js.map +1 -0
  84. package/dist/src/client.js +181 -0
  85. package/dist/src/client.js.map +1 -0
  86. package/dist/src/config.js +108 -0
  87. package/dist/src/config.js.map +1 -0
  88. package/dist/src/connectors/slack/backfill.js +76 -0
  89. package/dist/src/connectors/slack/backfill.js.map +1 -0
  90. package/dist/src/connectors/slack/deletion.js +46 -0
  91. package/dist/src/connectors/slack/deletion.js.map +1 -0
  92. package/dist/src/connectors/slack/dlq.js +23 -0
  93. package/dist/src/connectors/slack/dlq.js.map +1 -0
  94. package/dist/src/connectors/slack/idempotency.js +13 -0
  95. package/dist/src/connectors/slack/idempotency.js.map +1 -0
  96. package/dist/src/connectors/slack/ingest.js +48 -0
  97. package/dist/src/connectors/slack/ingest.js.map +1 -0
  98. package/dist/src/connectors/slack/ratelimit.js +18 -0
  99. package/dist/src/connectors/slack/ratelimit.js.map +1 -0
  100. package/dist/src/connectors/slack/scope.js +13 -0
  101. package/dist/src/connectors/slack/scope.js.map +1 -0
  102. package/dist/src/connectors/slack/signature.js +20 -0
  103. package/dist/src/connectors/slack/signature.js.map +1 -0
  104. package/dist/src/connectors/slack/tenant-routing.js +17 -0
  105. package/dist/src/connectors/slack/tenant-routing.js.map +1 -0
  106. package/dist/src/connectors/slack/transform.js +31 -0
  107. package/dist/src/connectors/slack/transform.js.map +1 -0
  108. package/dist/src/connectors/slack/types.js +23 -0
  109. package/dist/src/connectors/slack/types.js.map +1 -0
  110. package/dist/src/connectors/slack/web-client.js +43 -0
  111. package/dist/src/connectors/slack/web-client.js.map +1 -0
  112. package/dist/src/consolidate.js +517 -0
  113. package/dist/src/consolidate.js.map +1 -0
  114. package/dist/src/dag.js +104 -0
  115. package/dist/src/dag.js.map +1 -0
  116. package/dist/src/dashboard.js +409 -0
  117. package/dist/src/dashboard.js.map +1 -0
  118. package/dist/src/db.js +584 -0
  119. package/dist/src/db.js.map +1 -0
  120. package/dist/src/embeddings.js +344 -0
  121. package/dist/src/embeddings.js.map +1 -0
  122. package/dist/src/eval-suite.js +289 -0
  123. package/dist/src/eval-suite.js.map +1 -0
  124. package/dist/src/eval.js +187 -0
  125. package/dist/src/eval.js.map +1 -0
  126. package/dist/src/extract.js +87 -0
  127. package/dist/src/extract.js.map +1 -0
  128. package/dist/src/handoff.js +30 -0
  129. package/dist/src/handoff.js.map +1 -0
  130. package/dist/src/hooks.js +582 -0
  131. package/dist/src/hooks.js.map +1 -0
  132. package/dist/src/importers.js +399 -0
  133. package/dist/src/importers.js.map +1 -0
  134. package/dist/src/index.js +25 -0
  135. package/dist/src/index.js.map +1 -0
  136. package/dist/src/invalidation.js +94 -0
  137. package/dist/src/invalidation.js.map +1 -0
  138. package/dist/src/mcp/framing.js +45 -0
  139. package/dist/src/mcp/framing.js.map +1 -0
  140. package/dist/src/mcp/server.js +510 -0
  141. package/dist/src/mcp/server.js.map +1 -0
  142. package/dist/src/memory.js +280 -0
  143. package/dist/src/memory.js.map +1 -0
  144. package/dist/src/multihop.js +32 -0
  145. package/dist/src/multihop.js.map +1 -0
  146. package/dist/src/path-context.js +32 -0
  147. package/dist/src/path-context.js.map +1 -0
  148. package/dist/src/physics-config.js +26 -0
  149. package/dist/src/physics-config.js.map +1 -0
  150. package/dist/src/physics-state.js +163 -0
  151. package/dist/src/physics-state.js.map +1 -0
  152. package/dist/src/physics.js +361 -0
  153. package/dist/src/physics.js.map +1 -0
  154. package/dist/src/postinstall.js +68 -0
  155. package/dist/src/postinstall.js.map +1 -0
  156. package/dist/src/raw-archive.js +72 -0
  157. package/dist/src/raw-archive.js.map +1 -0
  158. package/dist/src/refine-llm.js +147 -0
  159. package/dist/src/refine-llm.js.map +1 -0
  160. package/dist/src/replay.js +117 -0
  161. package/dist/src/replay.js.map +1 -0
  162. package/dist/src/salience.js +74 -0
  163. package/dist/src/salience.js.map +1 -0
  164. package/dist/src/scheduler.js +67 -0
  165. package/dist/src/scheduler.js.map +1 -0
  166. package/dist/src/scope.js +35 -0
  167. package/dist/src/scope.js.map +1 -0
  168. package/dist/src/search.js +801 -0
  169. package/dist/src/search.js.map +1 -0
  170. package/dist/src/server-detect.js +70 -0
  171. package/dist/src/server-detect.js.map +1 -0
  172. package/dist/src/server.js +784 -0
  173. package/dist/src/server.js.map +1 -0
  174. package/dist/src/shared.js +309 -0
  175. package/dist/src/shared.js.map +1 -0
  176. package/dist/src/sso.js +22 -0
  177. package/dist/src/sso.js.map +1 -0
  178. package/dist/src/store.js +1390 -0
  179. package/dist/src/store.js.map +1 -0
  180. package/dist/src/tenant.js +17 -0
  181. package/dist/src/tenant.js.map +1 -0
  182. package/dist/src/trace.js +64 -0
  183. package/dist/src/trace.js.map +1 -0
  184. package/dist/src/working-memory.js +149 -0
  185. package/dist/src/working-memory.js.map +1 -0
  186. package/dist/src/yaml.js +98 -0
  187. package/dist/src/yaml.js.map +1 -0
  188. package/dist/store.d.ts +9 -1
  189. package/dist/store.d.ts.map +1 -1
  190. package/dist/store.js +30 -2
  191. package/dist/store.js.map +1 -1
  192. package/extensions/openclaw-plugin/openclaw.plugin.json +1 -1
  193. package/extensions/openclaw-plugin/package.json +1 -1
  194. package/openclaw.plugin.json +1 -1
  195. package/package.json +2 -2
  196. package/dist/import.d.ts +0 -31
  197. package/dist/import.d.ts.map +0 -1
  198. package/dist/import.js +0 -307
  199. 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
@@ -62,6 +62,9 @@ import { wmPush, wmRead, wmClear, wmFlush } from './working-memory.js';
62
62
  import { multihopSearch } from './multihop.js';
63
63
  import { computeSalience } from './salience.js';
64
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';
65
68
  // ---------------------------------------------------------------------------
66
69
  // Helpers
67
70
  // ---------------------------------------------------------------------------
@@ -4055,6 +4058,82 @@ function cmdAuth(hippoRoot, args, flags) {
4055
4058
  process.exit(1);
4056
4059
  }
4057
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
+ }
4058
4137
  function printUsage() {
4059
4138
  console.log(`
4060
4139
  Hippo - biologically-inspired memory system for AI agents
@@ -4488,6 +4567,9 @@ async function main() {
4488
4567
  case 'auth':
4489
4568
  cmdAuth(hippoRoot, args, flags);
4490
4569
  break;
4570
+ case 'slack':
4571
+ cmdSlack(hippoRoot, args, flags);
4572
+ break;
4491
4573
  case 'audit': {
4492
4574
  // `audit list` -> A5 audit-log viewer. Other forms (no sub, --fix) keep
4493
4575
  // the existing memory-quality auditor for backwards compatibility.