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,54 @@
1
+ /**
2
+ * HTTP client wrapper for `hippo serve`.
3
+ *
4
+ * Mirrors the function signatures in src/api.ts so the CLI can route through
5
+ * either path uniformly. Each function takes (serverUrl, apiKey?, ...) and
6
+ * returns the same shape that api.ts would.
7
+ *
8
+ * Errors from the server (4xx/5xx) are mapped back into thrown Errors with
9
+ * the server's `error` message preserved verbatim so existing CLI handlers
10
+ * that match on substrings (e.g. "not found", "already superseded") still
11
+ * work unchanged.
12
+ *
13
+ * Network errors (ECONNREFUSED on a stale pidfile, etc.) propagate as the
14
+ * native fetch failure so the caller can detect them and self-heal.
15
+ */
16
+ import type { AuditEvent } from './audit.js';
17
+ import type { ApiKeyListItem } from './auth.js';
18
+ import type { RememberOpts, RememberResult, RecallOpts, RecallResult, AuthCreateOpts, AuthCreateResult, AuditListOpts } from './api.js';
19
+ export declare function remember(serverUrl: string, apiKey: string | undefined, opts: RememberOpts): Promise<RememberResult>;
20
+ export declare function recall(serverUrl: string, apiKey: string | undefined, opts: RecallOpts): Promise<RecallResult>;
21
+ export declare function forget(serverUrl: string, apiKey: string | undefined, id: string): Promise<{
22
+ ok: true;
23
+ id: string;
24
+ }>;
25
+ export declare function promote(serverUrl: string, apiKey: string | undefined, id: string): Promise<{
26
+ ok: true;
27
+ sourceId: string;
28
+ globalId: string;
29
+ }>;
30
+ export declare function supersede(serverUrl: string, apiKey: string | undefined, oldId: string, newContent: string): Promise<{
31
+ ok: true;
32
+ oldId: string;
33
+ newId: string;
34
+ }>;
35
+ export declare function archiveRaw(serverUrl: string, apiKey: string | undefined, id: string, reason: string): Promise<{
36
+ ok: true;
37
+ archivedAt: string;
38
+ }>;
39
+ export declare function authCreate(serverUrl: string, apiKey: string | undefined, opts: AuthCreateOpts): Promise<AuthCreateResult>;
40
+ export declare function authList(serverUrl: string, apiKey: string | undefined, opts: {
41
+ active: boolean;
42
+ }): Promise<ApiKeyListItem[]>;
43
+ export declare function authRevoke(serverUrl: string, apiKey: string | undefined, keyId: string): Promise<{
44
+ ok: true;
45
+ revokedAt: string;
46
+ }>;
47
+ export declare function auditList(serverUrl: string, apiKey: string | undefined, opts: AuditListOpts): Promise<AuditEvent[]>;
48
+ /**
49
+ * True for fetch failures that look like "server not actually running" (the
50
+ * pidfile said one was, but the connection refused, or DNS / abort errors).
51
+ * The CLI uses this to detect a stale pidfile and self-heal back to direct mode.
52
+ */
53
+ export declare function isConnectionRefused(err: unknown): boolean;
54
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAW,MAAM,YAAY,CAAC;AACtD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAChD,OAAO,KAAK,EACV,YAAY,EACZ,cAAc,EACd,UAAU,EACV,YAAY,EACZ,cAAc,EACd,gBAAgB,EAChB,aAAa,EACd,MAAM,UAAU,CAAC;AA4BlB,wBAAsB,QAAQ,CAC5B,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,IAAI,EAAE,YAAY,GACjB,OAAO,CAAC,cAAc,CAAC,CAQzB;AAED,wBAAsB,MAAM,CAC1B,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,IAAI,EAAE,UAAU,GACf,OAAO,CAAC,YAAY,CAAC,CAWvB;AAED,wBAAsB,MAAM,CAC1B,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,EAAE,EAAE,MAAM,GACT,OAAO,CAAC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC,CAOnC;AAED,wBAAsB,OAAO,CAC3B,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,EAAE,EAAE,MAAM,GACT,OAAO,CAAC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAO3D;AAED,wBAAsB,SAAS,CAC7B,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,KAAK,EAAE,MAAM,EACb,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAQrD;AAED,wBAAsB,UAAU,CAC9B,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,EAAE,EAAE,MAAM,EACV,MAAM,EAAE,MAAM,GACb,OAAO,CAAC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC,CAQ3C;AAED,wBAAsB,UAAU,CAC9B,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,IAAI,EAAE,cAAc,GACnB,OAAO,CAAC,gBAAgB,CAAC,CAQ3B;AAED,wBAAsB,QAAQ,CAC5B,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,IAAI,EAAE;IAAE,MAAM,EAAE,OAAO,CAAA;CAAE,GACxB,OAAO,CAAC,cAAc,EAAE,CAAC,CAS3B;AAED,wBAAsB,UAAU,CAC9B,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,CAO1C;AAED,wBAAsB,SAAS,CAC7B,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,IAAI,EAAE,aAAa,GAClB,OAAO,CAAC,UAAU,EAAE,CAAC,CAavB;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAgBzD"}
package/dist/client.js ADDED
@@ -0,0 +1,181 @@
1
+ /**
2
+ * HTTP client wrapper for `hippo serve`.
3
+ *
4
+ * Mirrors the function signatures in src/api.ts so the CLI can route through
5
+ * either path uniformly. Each function takes (serverUrl, apiKey?, ...) and
6
+ * returns the same shape that api.ts would.
7
+ *
8
+ * Errors from the server (4xx/5xx) are mapped back into thrown Errors with
9
+ * the server's `error` message preserved verbatim so existing CLI handlers
10
+ * that match on substrings (e.g. "not found", "already superseded") still
11
+ * work unchanged.
12
+ *
13
+ * Network errors (ECONNREFUSED on a stale pidfile, etc.) propagate as the
14
+ * native fetch failure so the caller can detect them and self-heal.
15
+ */
16
+ function buildHeaders(apiKey, withBody) {
17
+ const headers = {};
18
+ if (withBody)
19
+ headers['content-type'] = 'application/json';
20
+ if (apiKey)
21
+ headers['authorization'] = `Bearer ${apiKey}`;
22
+ return headers;
23
+ }
24
+ /**
25
+ * Throw an Error matching the server's error message. Keeps message strings
26
+ * intact so cli.ts handlers can match on the same substrings ("not found",
27
+ * "already superseded", "Unknown key_id") whether the call went through
28
+ * api.ts or client.ts.
29
+ */
30
+ async function throwForStatus(res) {
31
+ let message = `${res.status} ${res.statusText}`;
32
+ try {
33
+ const body = await res.json();
34
+ if (body && typeof body.error === 'string' && body.error.length > 0) {
35
+ message = body.error;
36
+ }
37
+ }
38
+ catch {
39
+ // body wasn't JSON; fall back to status line.
40
+ }
41
+ throw new Error(message);
42
+ }
43
+ export async function remember(serverUrl, apiKey, opts) {
44
+ const res = await fetch(`${serverUrl}/v1/memories`, {
45
+ method: 'POST',
46
+ headers: buildHeaders(apiKey, true),
47
+ body: JSON.stringify(opts),
48
+ });
49
+ if (!res.ok)
50
+ await throwForStatus(res);
51
+ return await res.json();
52
+ }
53
+ export async function recall(serverUrl, apiKey, opts) {
54
+ const params = new URLSearchParams();
55
+ params.set('q', opts.query);
56
+ if (opts.limit !== undefined)
57
+ params.set('limit', String(opts.limit));
58
+ if (opts.mode !== undefined)
59
+ params.set('mode', opts.mode);
60
+ const res = await fetch(`${serverUrl}/v1/memories?${params.toString()}`, {
61
+ method: 'GET',
62
+ headers: buildHeaders(apiKey, false),
63
+ });
64
+ if (!res.ok)
65
+ await throwForStatus(res);
66
+ return await res.json();
67
+ }
68
+ export async function forget(serverUrl, apiKey, id) {
69
+ const res = await fetch(`${serverUrl}/v1/memories/${encodeURIComponent(id)}`, {
70
+ method: 'DELETE',
71
+ headers: buildHeaders(apiKey, false),
72
+ });
73
+ if (!res.ok)
74
+ await throwForStatus(res);
75
+ return await res.json();
76
+ }
77
+ export async function promote(serverUrl, apiKey, id) {
78
+ const res = await fetch(`${serverUrl}/v1/memories/${encodeURIComponent(id)}/promote`, {
79
+ method: 'POST',
80
+ headers: buildHeaders(apiKey, false),
81
+ });
82
+ if (!res.ok)
83
+ await throwForStatus(res);
84
+ return await res.json();
85
+ }
86
+ export async function supersede(serverUrl, apiKey, oldId, newContent) {
87
+ const res = await fetch(`${serverUrl}/v1/memories/${encodeURIComponent(oldId)}/supersede`, {
88
+ method: 'POST',
89
+ headers: buildHeaders(apiKey, true),
90
+ body: JSON.stringify({ content: newContent }),
91
+ });
92
+ if (!res.ok)
93
+ await throwForStatus(res);
94
+ return await res.json();
95
+ }
96
+ export async function archiveRaw(serverUrl, apiKey, id, reason) {
97
+ const res = await fetch(`${serverUrl}/v1/memories/${encodeURIComponent(id)}/archive`, {
98
+ method: 'POST',
99
+ headers: buildHeaders(apiKey, true),
100
+ body: JSON.stringify({ reason }),
101
+ });
102
+ if (!res.ok)
103
+ await throwForStatus(res);
104
+ return await res.json();
105
+ }
106
+ export async function authCreate(serverUrl, apiKey, opts) {
107
+ const res = await fetch(`${serverUrl}/v1/auth/keys`, {
108
+ method: 'POST',
109
+ headers: buildHeaders(apiKey, true),
110
+ body: JSON.stringify(opts),
111
+ });
112
+ if (!res.ok)
113
+ await throwForStatus(res);
114
+ return await res.json();
115
+ }
116
+ export async function authList(serverUrl, apiKey, opts) {
117
+ const params = new URLSearchParams();
118
+ params.set('active', opts.active ? 'true' : 'false');
119
+ const res = await fetch(`${serverUrl}/v1/auth/keys?${params.toString()}`, {
120
+ method: 'GET',
121
+ headers: buildHeaders(apiKey, false),
122
+ });
123
+ if (!res.ok)
124
+ await throwForStatus(res);
125
+ return await res.json();
126
+ }
127
+ export async function authRevoke(serverUrl, apiKey, keyId) {
128
+ const res = await fetch(`${serverUrl}/v1/auth/keys/${encodeURIComponent(keyId)}`, {
129
+ method: 'DELETE',
130
+ headers: buildHeaders(apiKey, false),
131
+ });
132
+ if (!res.ok)
133
+ await throwForStatus(res);
134
+ return await res.json();
135
+ }
136
+ export async function auditList(serverUrl, apiKey, opts) {
137
+ const params = new URLSearchParams();
138
+ if (opts.op !== undefined)
139
+ params.set('op', opts.op);
140
+ if (opts.since !== undefined)
141
+ params.set('since', opts.since);
142
+ if (opts.limit !== undefined)
143
+ params.set('limit', String(opts.limit));
144
+ const qs = params.toString();
145
+ const url = qs.length > 0 ? `${serverUrl}/v1/audit?${qs}` : `${serverUrl}/v1/audit`;
146
+ const res = await fetch(url, {
147
+ method: 'GET',
148
+ headers: buildHeaders(apiKey, false),
149
+ });
150
+ if (!res.ok)
151
+ await throwForStatus(res);
152
+ return await res.json();
153
+ }
154
+ /**
155
+ * True for fetch failures that look like "server not actually running" (the
156
+ * pidfile said one was, but the connection refused, or DNS / abort errors).
157
+ * The CLI uses this to detect a stale pidfile and self-heal back to direct mode.
158
+ */
159
+ export function isConnectionRefused(err) {
160
+ if (!(err instanceof Error))
161
+ return false;
162
+ const message = err.message.toLowerCase();
163
+ // Node fetch wraps the underlying cause; the surface message contains 'fetch failed'
164
+ // and the cause has the syscall code. We check both shapes.
165
+ if (message.includes('econnrefused'))
166
+ return true;
167
+ if (message.includes('connect econnrefused'))
168
+ return true;
169
+ const cause = err.cause;
170
+ if (cause && typeof cause === 'object') {
171
+ const code = cause.code;
172
+ if (code === 'ECONNREFUSED' || code === 'ECONNRESET')
173
+ return true;
174
+ }
175
+ // Fallthrough: 'fetch failed' alone is suspicious. Treat as connection failure
176
+ // so the CLI heals on a stale pidfile rather than surfacing a cryptic error.
177
+ if (message.includes('fetch failed'))
178
+ return true;
179
+ return false;
180
+ }
181
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,42 @@
1
+ import type { Context } from '../../api.js';
2
+ import type { SlackMessageEvent } from './types.js';
3
+ import type { ChannelMeta } from './scope.js';
4
+ export interface SlackHistoryPage {
5
+ messages: SlackMessageEvent[];
6
+ next_cursor: string | null;
7
+ }
8
+ export type SlackHistoryFetcher = (args: {
9
+ channelId: string;
10
+ /** Slack's opaque pagination token. Null on first page of a backfill run. */
11
+ cursor: string | null;
12
+ /**
13
+ * Incremental-resume bound: skip messages with ts <= oldest. Set from
14
+ * `slack_cursors.latest_ts` on the FIRST page of a rerun; unused on
15
+ * subsequent pages within one run (where `cursor` carries us forward).
16
+ * Distinct from `cursor` because Slack treats `cursor` as opaque token
17
+ * and `oldest` as a numeric ts — feeding `latest_ts` as `cursor` breaks
18
+ * against the live API even though it round-trips through test fetchers.
19
+ */
20
+ oldest?: string;
21
+ }) => Promise<SlackHistoryPage>;
22
+ export interface BackfillOpts {
23
+ teamId: string;
24
+ channel: ChannelMeta;
25
+ fetcher: SlackHistoryFetcher;
26
+ /** Stop after this many messages. Default: unlimited. */
27
+ maxMessages?: number;
28
+ }
29
+ /**
30
+ * Page through `conversations.history` via the injected fetcher and ingest each
31
+ * message. The cursor is persisted to `slack_cursors` after every page so a
32
+ * crash mid-backfill resumes near where it left off.
33
+ *
34
+ * Each ingested message uses a synthesized eventId of the form
35
+ * `backfill:${teamId}:${channelId}:${ts}`. Reruns dedupe via the
36
+ * `slack_event_log` PK so calling `backfillChannel` twice is safe.
37
+ */
38
+ export declare function backfillChannel(ctx: Context, opts: BackfillOpts): Promise<{
39
+ ingested: number;
40
+ pages: number;
41
+ }>;
42
+ //# sourceMappingURL=backfill.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backfill.d.ts","sourceRoot":"","sources":["../../../src/connectors/slack/backfill.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAG5C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AACpD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,iBAAiB,EAAE,CAAC;IAC9B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED,MAAM,MAAM,mBAAmB,GAAG,CAAC,IAAI,EAAE;IACvC,SAAS,EAAE,MAAM,CAAC;IAClB,6EAA6E;IAC7E,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,KAAK,OAAO,CAAC,gBAAgB,CAAC,CAAC;AAEhC,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,WAAW,CAAC;IACrB,OAAO,EAAE,mBAAmB,CAAC;IAC7B,yDAAyD;IACzD,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AA+BD;;;;;;;;GAQG;AACH,wBAAsB,eAAe,CACnC,GAAG,EAAE,OAAO,EACZ,IAAI,EAAE,YAAY,GACjB,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAoC9C"}
@@ -0,0 +1,76 @@
1
+ import { openHippoDb, closeHippoDb } from '../../db.js';
2
+ import { ingestMessage } from './ingest.js';
3
+ function readCursor(root, tenantId, channelId) {
4
+ const db = openHippoDb(root);
5
+ try {
6
+ const row = db
7
+ .prepare(`SELECT latest_ts FROM slack_cursors WHERE tenant_id=? AND channel_id=?`)
8
+ .get(tenantId, channelId);
9
+ return row?.latest_ts ?? null;
10
+ }
11
+ finally {
12
+ closeHippoDb(db);
13
+ }
14
+ }
15
+ function writeCursor(root, tenantId, channelId, latestTs) {
16
+ const db = openHippoDb(root);
17
+ try {
18
+ db.prepare(`INSERT INTO slack_cursors (tenant_id, channel_id, latest_ts, updated_at) VALUES (?,?,?,?)
19
+ ON CONFLICT(tenant_id, channel_id) DO UPDATE SET latest_ts = excluded.latest_ts, updated_at = excluded.updated_at`).run(tenantId, channelId, latestTs, new Date().toISOString());
20
+ }
21
+ finally {
22
+ closeHippoDb(db);
23
+ }
24
+ }
25
+ /**
26
+ * Page through `conversations.history` via the injected fetcher and ingest each
27
+ * message. The cursor is persisted to `slack_cursors` after every page so a
28
+ * crash mid-backfill resumes near where it left off.
29
+ *
30
+ * Each ingested message uses a synthesized eventId of the form
31
+ * `backfill:${teamId}:${channelId}:${ts}`. Reruns dedupe via the
32
+ * `slack_event_log` PK so calling `backfillChannel` twice is safe.
33
+ */
34
+ export async function backfillChannel(ctx, opts) {
35
+ // Resume bound from previous run; passed as `oldest` (numeric ts) on the
36
+ // first page only. `cursor` starts null — Slack mints the next-page token
37
+ // and we feed it back. Mixing the two would feed a numeric ts as an opaque
38
+ // cursor and break against the live API on rerun.
39
+ const resumeFrom = readCursor(ctx.hippoRoot, ctx.tenantId, opts.channel.id);
40
+ let cursor = null;
41
+ let ingested = 0;
42
+ let pages = 0;
43
+ let latestTs = resumeFrom;
44
+ while (true) {
45
+ const page = await opts.fetcher({
46
+ channelId: opts.channel.id,
47
+ cursor,
48
+ oldest: pages === 0 && resumeFrom ? resumeFrom : undefined,
49
+ });
50
+ pages++;
51
+ for (const msg of page.messages) {
52
+ const r = ingestMessage(ctx, {
53
+ teamId: opts.teamId,
54
+ channel: opts.channel,
55
+ message: msg,
56
+ eventId: `backfill:${opts.teamId}:${opts.channel.id}:${msg.ts}`,
57
+ });
58
+ if (r.status === 'ingested')
59
+ ingested++;
60
+ if (!latestTs || msg.ts > latestTs)
61
+ latestTs = msg.ts;
62
+ if (opts.maxMessages && ingested >= opts.maxMessages) {
63
+ if (latestTs)
64
+ writeCursor(ctx.hippoRoot, ctx.tenantId, opts.channel.id, latestTs);
65
+ return { ingested, pages };
66
+ }
67
+ }
68
+ if (latestTs)
69
+ writeCursor(ctx.hippoRoot, ctx.tenantId, opts.channel.id, latestTs);
70
+ if (!page.next_cursor)
71
+ break;
72
+ cursor = page.next_cursor;
73
+ }
74
+ return { ingested, pages };
75
+ }
76
+ //# sourceMappingURL=backfill.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backfill.js","sourceRoot":"","sources":["../../../src/connectors/slack/backfill.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import { type Context } from '../../api.js';
2
+ export interface DeletionInput {
3
+ teamId: string;
4
+ channelId: string;
5
+ deletedTs: string;
6
+ eventId: string;
7
+ }
8
+ export type DeletionStatus = 'archived' | 'not_found' | 'duplicate';
9
+ export interface DeletionResult {
10
+ status: DeletionStatus;
11
+ memoryId: string | null;
12
+ }
13
+ export declare function handleMessageDeleted(ctx: Context, input: DeletionInput): DeletionResult;
14
+ //# sourceMappingURL=deletion.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deletion.d.ts","sourceRoot":"","sources":["../../../src/connectors/slack/deletion.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,KAAK,OAAO,EAAE,MAAM,cAAc,CAAC;AAIxD,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,MAAM,cAAc,GAAG,UAAU,GAAG,WAAW,GAAG,WAAW,CAAC;AAEpE,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,cAAc,CAAC;IACvB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,aAAa,GAAG,cAAc,CAgCvF"}
@@ -0,0 +1,46 @@
1
+ import { archiveRaw } from '../../api.js';
2
+ import { openHippoDb, closeHippoDb } from '../../db.js';
3
+ import { markEventSeen, hasSeenEvent } from './idempotency.js';
4
+ export function handleMessageDeleted(ctx, input) {
5
+ const db = openHippoDb(ctx.hippoRoot);
6
+ let memoryId = null;
7
+ try {
8
+ if (hasSeenEvent(db, input.eventId)) {
9
+ return { status: 'duplicate', memoryId: null };
10
+ }
11
+ const ref = `slack://${input.teamId}/${input.channelId}/${input.deletedTs}`;
12
+ // Tenant scope is load-bearing: without `tenant_id = ?` a deletion event
13
+ // from tenant A could archive tenant B's raw row sharing the same
14
+ // artifact_ref. The `kind = 'raw'` filter prevents accidentally targeting
15
+ // distilled rows.
16
+ const row = db
17
+ .prepare(`SELECT id FROM memories WHERE artifact_ref = ? AND tenant_id = ? AND kind = 'raw'`)
18
+ .get(ref, ctx.tenantId);
19
+ memoryId = row?.id ?? null;
20
+ }
21
+ finally {
22
+ closeHippoDb(db);
23
+ }
24
+ if (!memoryId) {
25
+ const db2 = openHippoDb(ctx.hippoRoot);
26
+ try {
27
+ markEventSeen(db2, input.eventId, null);
28
+ }
29
+ finally {
30
+ closeHippoDb(db2);
31
+ }
32
+ return { status: 'not_found', memoryId: null };
33
+ }
34
+ // api.archiveRaw now handles legacy mirror cleanup centrally so every caller
35
+ // (CLI, REST route, MCP tool, this connector) gets the GDPR-correct archive.
36
+ archiveRaw(ctx, memoryId, `source_deleted:slack:${input.teamId}:${input.channelId}:${input.deletedTs}`);
37
+ const db3 = openHippoDb(ctx.hippoRoot);
38
+ try {
39
+ markEventSeen(db3, input.eventId, memoryId);
40
+ }
41
+ finally {
42
+ closeHippoDb(db3);
43
+ }
44
+ return { status: 'archived', memoryId };
45
+ }
46
+ //# sourceMappingURL=deletion.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deletion.js","sourceRoot":"","sources":["../../../src/connectors/slack/deletion.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAgB,MAAM,cAAc,CAAC;AACxD,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAgB/D,MAAM,UAAU,oBAAoB,CAAC,GAAY,EAAE,KAAoB;IACrE,MAAM,EAAE,GAAG,WAAW,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IACtC,IAAI,QAAQ,GAAkB,IAAI,CAAC;IACnC,IAAI,CAAC;QACH,IAAI,YAAY,CAAC,EAAE,EAAE,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;YACpC,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;QACjD,CAAC;QACD,MAAM,GAAG,GAAG,WAAW,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,SAAS,IAAI,KAAK,CAAC,SAAS,EAAE,CAAC;QAC5E,yEAAyE;QACzE,kEAAkE;QAClE,0EAA0E;QAC1E,kBAAkB;QAClB,MAAM,GAAG,GAAG,EAAE;aACX,OAAO,CAAC,mFAAmF,CAAC;aAC5F,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,QAAQ,CAAgC,CAAC;QACzD,QAAQ,GAAG,GAAG,EAAE,EAAE,IAAI,IAAI,CAAC;IAC7B,CAAC;YAAS,CAAC;QACT,YAAY,CAAC,EAAE,CAAC,CAAC;IACnB,CAAC;IACD,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,MAAM,GAAG,GAAG,WAAW,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QACvC,IAAI,CAAC;YAAC,aAAa,CAAC,GAAG,EAAE,KAAK,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;QAAC,CAAC;gBACxC,CAAC;YAAC,YAAY,CAAC,GAAG,CAAC,CAAC;QAAC,CAAC;QAC9B,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IACjD,CAAC;IACD,6EAA6E;IAC7E,6EAA6E;IAC7E,UAAU,CAAC,GAAG,EAAE,QAAQ,EAAE,wBAAwB,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,SAAS,IAAI,KAAK,CAAC,SAAS,EAAE,CAAC,CAAC;IACxG,MAAM,GAAG,GAAG,WAAW,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IACvC,IAAI,CAAC;QAAC,aAAa,CAAC,GAAG,EAAE,KAAK,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;IAAC,CAAC;YAC5C,CAAC;QAAC,YAAY,CAAC,GAAG,CAAC,CAAC;IAAC,CAAC;IAC9B,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,CAAC;AAC1C,CAAC"}
@@ -0,0 +1,21 @@
1
+ import type { DatabaseSyncLike } from '../../db.js';
2
+ export interface DlqItem {
3
+ id: number;
4
+ tenantId: string;
5
+ rawPayload: string;
6
+ error: string;
7
+ receivedAt: string;
8
+ retriedAt: string | null;
9
+ }
10
+ export interface WriteDlqOpts {
11
+ tenantId: string;
12
+ rawPayload: string;
13
+ error: string;
14
+ }
15
+ export declare function writeToDlq(db: DatabaseSyncLike, opts: WriteDlqOpts): number;
16
+ export declare function listDlq(db: DatabaseSyncLike, opts: {
17
+ tenantId: string;
18
+ limit?: number;
19
+ }): DlqItem[];
20
+ export declare function markDlqRetried(db: DatabaseSyncLike, id: number): void;
21
+ //# sourceMappingURL=dlq.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dlq.d.ts","sourceRoot":"","sources":["../../../src/connectors/slack/dlq.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEpD,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,wBAAgB,UAAU,CAAC,EAAE,EAAE,gBAAgB,EAAE,IAAI,EAAE,YAAY,GAAG,MAAM,CAK3E;AAED,wBAAgB,OAAO,CAAC,EAAE,EAAE,gBAAgB,EAAE,IAAI,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,EAAE,CAYnG;AAED,wBAAgB,cAAc,CAAC,EAAE,EAAE,gBAAgB,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI,CAErE"}
@@ -0,0 +1,23 @@
1
+ export function writeToDlq(db, opts) {
2
+ const result = db
3
+ .prepare(`INSERT INTO slack_dlq (tenant_id, raw_payload, error, received_at) VALUES (?, ?, ?, ?)`)
4
+ .run(opts.tenantId, opts.rawPayload, opts.error, new Date().toISOString());
5
+ return Number(result.lastInsertRowid);
6
+ }
7
+ export function listDlq(db, opts) {
8
+ const rows = db
9
+ .prepare(`SELECT id, tenant_id, raw_payload, error, received_at, retried_at FROM slack_dlq WHERE tenant_id = ? ORDER BY received_at ASC LIMIT ?`)
10
+ .all(opts.tenantId, opts.limit ?? 100);
11
+ return rows.map((r) => ({
12
+ id: Number(r.id),
13
+ tenantId: String(r.tenant_id),
14
+ rawPayload: String(r.raw_payload),
15
+ error: String(r.error),
16
+ receivedAt: String(r.received_at),
17
+ retriedAt: r.retried_at == null ? null : String(r.retried_at),
18
+ }));
19
+ }
20
+ export function markDlqRetried(db, id) {
21
+ db.prepare(`UPDATE slack_dlq SET retried_at = ? WHERE id = ?`).run(new Date().toISOString(), id);
22
+ }
23
+ //# sourceMappingURL=dlq.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dlq.js","sourceRoot":"","sources":["../../../src/connectors/slack/dlq.ts"],"names":[],"mappings":"AAiBA,MAAM,UAAU,UAAU,CAAC,EAAoB,EAAE,IAAkB;IACjE,MAAM,MAAM,GAAG,EAAE;SACd,OAAO,CAAC,wFAAwF,CAAC;SACjG,GAAG,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC;IAC7E,OAAO,MAAM,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC;AACxC,CAAC;AAED,MAAM,UAAU,OAAO,CAAC,EAAoB,EAAE,IAA0C;IACtF,MAAM,IAAI,GAAG,EAAE;SACZ,OAAO,CAAC,uIAAuI,CAAC;SAChJ,GAAG,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,KAAK,IAAI,GAAG,CAAmC,CAAC;IAC3E,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACtB,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;QAChB,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;QAC7B,UAAU,EAAE,MAAM,CAAC,CAAC,CAAC,WAAW,CAAC;QACjC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC;QACtB,UAAU,EAAE,MAAM,CAAC,CAAC,CAAC,WAAW,CAAC;QACjC,SAAS,EAAE,CAAC,CAAC,UAAU,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC;KAC9D,CAAC,CAAC,CAAC;AACN,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,EAAoB,EAAE,EAAU;IAC7D,EAAE,CAAC,OAAO,CAAC,kDAAkD,CAAC,CAAC,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,EAAE,CAAC,CAAC;AACnG,CAAC"}
@@ -0,0 +1,5 @@
1
+ import type { DatabaseSyncLike } from '../../db.js';
2
+ export declare function hasSeenEvent(db: DatabaseSyncLike, eventId: string): boolean;
3
+ export declare function markEventSeen(db: DatabaseSyncLike, eventId: string, memoryId: string | null): void;
4
+ export declare function lookupMemoryByEvent(db: DatabaseSyncLike, eventId: string): string | null;
5
+ //# sourceMappingURL=idempotency.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"idempotency.d.ts","sourceRoot":"","sources":["../../../src/connectors/slack/idempotency.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEpD,wBAAgB,YAAY,CAAC,EAAE,EAAE,gBAAgB,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAG3E;AAED,wBAAgB,aAAa,CAAC,EAAE,EAAE,gBAAgB,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAGlG;AAED,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,gBAAgB,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAKxF"}
@@ -0,0 +1,13 @@
1
+ export function hasSeenEvent(db, eventId) {
2
+ const row = db.prepare(`SELECT 1 FROM slack_event_log WHERE event_id = ?`).get(eventId);
3
+ return !!row;
4
+ }
5
+ export function markEventSeen(db, eventId, memoryId) {
6
+ db.prepare(`INSERT OR IGNORE INTO slack_event_log (event_id, ingested_at, memory_id) VALUES (?, ?, ?)`)
7
+ .run(eventId, new Date().toISOString(), memoryId);
8
+ }
9
+ export function lookupMemoryByEvent(db, eventId) {
10
+ const row = db.prepare(`SELECT memory_id FROM slack_event_log WHERE event_id = ?`).get(eventId);
11
+ return row?.memory_id ?? null;
12
+ }
13
+ //# sourceMappingURL=idempotency.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"idempotency.js","sourceRoot":"","sources":["../../../src/connectors/slack/idempotency.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,YAAY,CAAC,EAAoB,EAAE,OAAe;IAChE,MAAM,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,kDAAkD,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IACxF,OAAO,CAAC,CAAC,GAAG,CAAC;AACf,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,EAAoB,EAAE,OAAe,EAAE,QAAuB;IAC1F,EAAE,CAAC,OAAO,CAAC,2FAA2F,CAAC;SACpG,GAAG,CAAC,OAAO,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,QAAQ,CAAC,CAAC;AACtD,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,EAAoB,EAAE,OAAe;IACvE,MAAM,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,0DAA0D,CAAC,CAAC,GAAG,CAAC,OAAO,CAEjF,CAAC;IACd,OAAO,GAAG,EAAE,SAAS,IAAI,IAAI,CAAC;AAChC,CAAC"}
@@ -0,0 +1,27 @@
1
+ import { type Context } from '../../api.js';
2
+ import type { ChannelMeta } from './scope.js';
3
+ import type { SlackMessageEvent } from './types.js';
4
+ export interface IngestInput {
5
+ teamId: string;
6
+ channel: ChannelMeta;
7
+ message: SlackMessageEvent;
8
+ /** Slack event_id for the envelope (or for backfill, a synthesized stable id). */
9
+ eventId: string;
10
+ }
11
+ export type IngestStatus = 'ingested' | 'duplicate' | 'skipped';
12
+ export interface IngestResult {
13
+ status: IngestStatus;
14
+ memoryId: string | null;
15
+ }
16
+ /**
17
+ * Ingest a Slack message into hippo as a kind='raw' memory.
18
+ *
19
+ * - Idempotency-checked via slack_event_log (Slack retries within 1 minute).
20
+ * - The memory write and the slack_event_log mark commit atomically through
21
+ * `api.remember`'s `afterWrite` hook — a crash between the two cannot
22
+ * produce a duplicate on the next retry.
23
+ * - Empty-body messages return 'skipped' but still mark seen so a replay
24
+ * returns 'duplicate' rather than re-running the transform.
25
+ */
26
+ export declare function ingestMessage(ctx: Context, input: IngestInput): IngestResult;
27
+ //# sourceMappingURL=ingest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ingest.d.ts","sourceRoot":"","sources":["../../../src/connectors/slack/ingest.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,KAAK,OAAO,EAAE,MAAM,cAAc,CAAC;AAItD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAEpD,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,WAAW,CAAC;IACrB,OAAO,EAAE,iBAAiB,CAAC;IAC3B,kFAAkF;IAClF,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,MAAM,YAAY,GAAG,UAAU,GAAG,WAAW,GAAG,SAAS,CAAC;AAEhE,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,YAAY,CAAC;IACrB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,WAAW,GAAG,YAAY,CAmC5E"}
@@ -0,0 +1,48 @@
1
+ import { remember } from '../../api.js';
2
+ import { openHippoDb, closeHippoDb } from '../../db.js';
3
+ import { hasSeenEvent, markEventSeen, lookupMemoryByEvent } from './idempotency.js';
4
+ import { messageToRememberOpts } from './transform.js';
5
+ /**
6
+ * Ingest a Slack message into hippo as a kind='raw' memory.
7
+ *
8
+ * - Idempotency-checked via slack_event_log (Slack retries within 1 minute).
9
+ * - The memory write and the slack_event_log mark commit atomically through
10
+ * `api.remember`'s `afterWrite` hook — a crash between the two cannot
11
+ * produce a duplicate on the next retry.
12
+ * - Empty-body messages return 'skipped' but still mark seen so a replay
13
+ * returns 'duplicate' rather than re-running the transform.
14
+ */
15
+ export function ingestMessage(ctx, input) {
16
+ // Idempotency check: if already seen, return the cached memory_id without
17
+ // re-running the transform or hitting api.remember.
18
+ const db = openHippoDb(ctx.hippoRoot);
19
+ try {
20
+ if (hasSeenEvent(db, input.eventId)) {
21
+ return { status: 'duplicate', memoryId: lookupMemoryByEvent(db, input.eventId) };
22
+ }
23
+ }
24
+ finally {
25
+ closeHippoDb(db);
26
+ }
27
+ const opts = messageToRememberOpts(input);
28
+ if (!opts) {
29
+ const db2 = openHippoDb(ctx.hippoRoot);
30
+ try {
31
+ markEventSeen(db2, input.eventId, null);
32
+ }
33
+ finally {
34
+ closeHippoDb(db2);
35
+ }
36
+ return { status: 'skipped', memoryId: null };
37
+ }
38
+ // Atomic write: the afterWrite callback runs inside writeEntry's SAVEPOINT,
39
+ // so the memory row and the slack_event_log row commit (or roll back)
40
+ // together. Slack's 1-minute retry window can no longer produce a duplicate
41
+ // via the crash-between-handles race.
42
+ const result = remember({ ...ctx, actor: ctx.actor || 'connector:slack' }, {
43
+ ...opts,
44
+ afterWrite: (db, memoryId) => markEventSeen(db, input.eventId, memoryId),
45
+ });
46
+ return { status: 'ingested', memoryId: result.id };
47
+ }
48
+ //# sourceMappingURL=ingest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ingest.js","sourceRoot":"","sources":["../../../src/connectors/slack/ingest.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAgB,MAAM,cAAc,CAAC;AACtD,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AACpF,OAAO,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AAmBvD;;;;;;;;;GASG;AACH,MAAM,UAAU,aAAa,CAAC,GAAY,EAAE,KAAkB;IAC5D,0EAA0E;IAC1E,oDAAoD;IACpD,MAAM,EAAE,GAAG,WAAW,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IACtC,IAAI,CAAC;QACH,IAAI,YAAY,CAAC,EAAE,EAAE,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;YACpC,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,QAAQ,EAAE,mBAAmB,CAAC,EAAE,EAAE,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;QACnF,CAAC;IACH,CAAC;YAAS,CAAC;QACT,YAAY,CAAC,EAAE,CAAC,CAAC;IACnB,CAAC;IAED,MAAM,IAAI,GAAG,qBAAqB,CAAC,KAAK,CAAC,CAAC;IAC1C,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,MAAM,GAAG,GAAG,WAAW,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QACvC,IAAI,CAAC;YACH,aAAa,CAAC,GAAG,EAAE,KAAK,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;QAC1C,CAAC;gBAAS,CAAC;YACT,YAAY,CAAC,GAAG,CAAC,CAAC;QACpB,CAAC;QACD,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC/C,CAAC;IAED,4EAA4E;IAC5E,sEAAsE;IACtE,4EAA4E;IAC5E,sCAAsC;IACtC,MAAM,MAAM,GAAG,QAAQ,CACrB,EAAE,GAAG,GAAG,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,IAAI,iBAAiB,EAAE,EACjD;QACE,GAAG,IAAI;QACP,UAAU,EAAE,CAAC,EAAE,EAAE,QAAQ,EAAE,EAAE,CAAC,aAAa,CAAC,EAAE,EAAE,KAAK,CAAC,OAAO,EAAE,QAAQ,CAAC;KACzE,CACF,CAAC;IACF,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,CAAC,EAAE,EAAE,CAAC;AACrD,CAAC"}
@@ -0,0 +1,9 @@
1
+ export interface RetryOpts {
2
+ url: string;
3
+ init?: RequestInit;
4
+ fetchImpl?: typeof fetch;
5
+ sleep?: (ms: number) => Promise<void>;
6
+ maxRetries?: number;
7
+ }
8
+ export declare function fetchWithRetry(opts: RetryOpts): Promise<Response>;
9
+ //# sourceMappingURL=ratelimit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ratelimit.d.ts","sourceRoot":"","sources":["../../../src/connectors/slack/ratelimit.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,SAAS;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,WAAW,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,wBAAsB,cAAc,CAAC,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC,QAAQ,CAAC,CAcvE"}