hippo-memory 0.36.0 → 0.38.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +62 -254
- package/dist/api.d.ts +20 -0
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +23 -3
- package/dist/api.js.map +1 -1
- package/dist/benchmarks/e1.3/incident-recall-eval.js +74 -0
- package/dist/benchmarks/e1.3/incident-recall-eval.js.map +1 -0
- package/dist/benchmarks/e1.3/scenarios.json +2587 -0
- package/dist/benchmarks/e1.3/slack-1000-event-smoke.js +102 -0
- package/dist/benchmarks/e1.3/slack-1000-event-smoke.js.map +1 -0
- package/dist/cli.js +449 -0
- package/dist/cli.js.map +1 -1
- package/dist/connectors/slack/backfill.d.ts +42 -0
- package/dist/connectors/slack/backfill.d.ts.map +1 -0
- package/dist/connectors/slack/backfill.js +76 -0
- package/dist/connectors/slack/backfill.js.map +1 -0
- package/dist/connectors/slack/deletion.d.ts +14 -0
- package/dist/connectors/slack/deletion.d.ts.map +1 -0
- package/dist/connectors/slack/deletion.js +46 -0
- package/dist/connectors/slack/deletion.js.map +1 -0
- package/dist/connectors/slack/dlq.d.ts +21 -0
- package/dist/connectors/slack/dlq.d.ts.map +1 -0
- package/dist/connectors/slack/dlq.js +23 -0
- package/dist/connectors/slack/dlq.js.map +1 -0
- package/dist/connectors/slack/idempotency.d.ts +5 -0
- package/dist/connectors/slack/idempotency.d.ts.map +1 -0
- package/dist/connectors/slack/idempotency.js +13 -0
- package/dist/connectors/slack/idempotency.js.map +1 -0
- package/dist/connectors/slack/ingest.d.ts +27 -0
- package/dist/connectors/slack/ingest.d.ts.map +1 -0
- package/dist/connectors/slack/ingest.js +48 -0
- package/dist/connectors/slack/ingest.js.map +1 -0
- package/dist/connectors/slack/ratelimit.d.ts +9 -0
- package/dist/connectors/slack/ratelimit.d.ts.map +1 -0
- package/dist/connectors/slack/ratelimit.js +18 -0
- package/dist/connectors/slack/ratelimit.js.map +1 -0
- package/dist/connectors/slack/scope.d.ts +16 -0
- package/dist/connectors/slack/scope.d.ts.map +1 -0
- package/dist/connectors/slack/scope.js +13 -0
- package/dist/connectors/slack/scope.js.map +1 -0
- package/dist/connectors/slack/signature.d.ts +12 -0
- package/dist/connectors/slack/signature.d.ts.map +1 -0
- package/dist/connectors/slack/signature.js +20 -0
- package/dist/connectors/slack/signature.js.map +1 -0
- package/dist/connectors/slack/tenant-routing.d.ts +13 -0
- package/dist/connectors/slack/tenant-routing.d.ts.map +1 -0
- package/dist/connectors/slack/tenant-routing.js +17 -0
- package/dist/connectors/slack/tenant-routing.js.map +1 -0
- package/dist/connectors/slack/transform.d.ts +20 -0
- package/dist/connectors/slack/transform.d.ts.map +1 -0
- package/dist/connectors/slack/transform.js +31 -0
- package/dist/connectors/slack/transform.js.map +1 -0
- package/dist/connectors/slack/types.d.ts +35 -0
- package/dist/connectors/slack/types.d.ts.map +1 -0
- package/dist/connectors/slack/types.js +23 -0
- package/dist/connectors/slack/types.js.map +1 -0
- package/dist/connectors/slack/web-client.d.ts +12 -0
- package/dist/connectors/slack/web-client.d.ts.map +1 -0
- package/dist/connectors/slack/web-client.js +43 -0
- package/dist/connectors/slack/web-client.js.map +1 -0
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +105 -1
- package/dist/db.js.map +1 -1
- package/dist/goals.d.ts +73 -0
- package/dist/goals.d.ts.map +1 -0
- package/dist/goals.js +227 -0
- package/dist/goals.js.map +1 -0
- package/dist/importers.js +3 -3
- package/dist/importers.js.map +1 -1
- package/dist/mcp/server.js +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +174 -2
- package/dist/server.js.map +1 -1
- package/dist/src/ambient.js +147 -0
- package/dist/src/ambient.js.map +1 -0
- package/dist/src/api.js +343 -0
- package/dist/src/api.js.map +1 -0
- package/dist/src/audit.js +152 -0
- package/dist/src/audit.js.map +1 -0
- package/dist/src/auth.js +65 -0
- package/dist/src/auth.js.map +1 -0
- package/dist/src/autolearn.js +143 -0
- package/dist/src/autolearn.js.map +1 -0
- package/dist/src/capture.js +512 -0
- package/dist/src/capture.js.map +1 -0
- package/dist/src/cli.js +5338 -0
- package/dist/src/cli.js.map +1 -0
- package/dist/src/client.js +181 -0
- package/dist/src/client.js.map +1 -0
- package/dist/src/config.js +108 -0
- package/dist/src/config.js.map +1 -0
- package/dist/src/connectors/slack/backfill.js +76 -0
- package/dist/src/connectors/slack/backfill.js.map +1 -0
- package/dist/src/connectors/slack/deletion.js +46 -0
- package/dist/src/connectors/slack/deletion.js.map +1 -0
- package/dist/src/connectors/slack/dlq.js +23 -0
- package/dist/src/connectors/slack/dlq.js.map +1 -0
- package/dist/src/connectors/slack/idempotency.js +13 -0
- package/dist/src/connectors/slack/idempotency.js.map +1 -0
- package/dist/src/connectors/slack/ingest.js +48 -0
- package/dist/src/connectors/slack/ingest.js.map +1 -0
- package/dist/src/connectors/slack/ratelimit.js +18 -0
- package/dist/src/connectors/slack/ratelimit.js.map +1 -0
- package/dist/src/connectors/slack/scope.js +13 -0
- package/dist/src/connectors/slack/scope.js.map +1 -0
- package/dist/src/connectors/slack/signature.js +20 -0
- package/dist/src/connectors/slack/signature.js.map +1 -0
- package/dist/src/connectors/slack/tenant-routing.js +17 -0
- package/dist/src/connectors/slack/tenant-routing.js.map +1 -0
- package/dist/src/connectors/slack/transform.js +31 -0
- package/dist/src/connectors/slack/transform.js.map +1 -0
- package/dist/src/connectors/slack/types.js +23 -0
- package/dist/src/connectors/slack/types.js.map +1 -0
- package/dist/src/connectors/slack/web-client.js +43 -0
- package/dist/src/connectors/slack/web-client.js.map +1 -0
- package/dist/src/consolidate.js +517 -0
- package/dist/src/consolidate.js.map +1 -0
- package/dist/src/dag.js +104 -0
- package/dist/src/dag.js.map +1 -0
- package/dist/src/dashboard.js +409 -0
- package/dist/src/dashboard.js.map +1 -0
- package/dist/src/db.js +643 -0
- package/dist/src/db.js.map +1 -0
- package/dist/src/embeddings.js +344 -0
- package/dist/src/embeddings.js.map +1 -0
- package/dist/src/eval-suite.js +289 -0
- package/dist/src/eval-suite.js.map +1 -0
- package/dist/src/eval.js +187 -0
- package/dist/src/eval.js.map +1 -0
- package/dist/src/extract.js +87 -0
- package/dist/src/extract.js.map +1 -0
- package/dist/src/goals.js +227 -0
- package/dist/src/goals.js.map +1 -0
- package/dist/src/handoff.js +30 -0
- package/dist/src/handoff.js.map +1 -0
- package/dist/src/hooks.js +582 -0
- package/dist/src/hooks.js.map +1 -0
- package/dist/src/importers.js +399 -0
- package/dist/src/importers.js.map +1 -0
- package/dist/src/index.js +25 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/invalidation.js +94 -0
- package/dist/src/invalidation.js.map +1 -0
- package/dist/src/mcp/framing.js +45 -0
- package/dist/src/mcp/framing.js.map +1 -0
- package/dist/src/mcp/server.js +510 -0
- package/dist/src/mcp/server.js.map +1 -0
- package/dist/src/memory.js +280 -0
- package/dist/src/memory.js.map +1 -0
- package/dist/src/multihop.js +32 -0
- package/dist/src/multihop.js.map +1 -0
- package/dist/src/path-context.js +32 -0
- package/dist/src/path-context.js.map +1 -0
- package/dist/src/physics-config.js +26 -0
- package/dist/src/physics-config.js.map +1 -0
- package/dist/src/physics-state.js +163 -0
- package/dist/src/physics-state.js.map +1 -0
- package/dist/src/physics.js +361 -0
- package/dist/src/physics.js.map +1 -0
- package/dist/src/postinstall.js +68 -0
- package/dist/src/postinstall.js.map +1 -0
- package/dist/src/raw-archive.js +72 -0
- package/dist/src/raw-archive.js.map +1 -0
- package/dist/src/refine-llm.js +147 -0
- package/dist/src/refine-llm.js.map +1 -0
- package/dist/src/replay.js +117 -0
- package/dist/src/replay.js.map +1 -0
- package/dist/src/salience.js +74 -0
- package/dist/src/salience.js.map +1 -0
- package/dist/src/scheduler.js +67 -0
- package/dist/src/scheduler.js.map +1 -0
- package/dist/src/scope.js +35 -0
- package/dist/src/scope.js.map +1 -0
- package/dist/src/search.js +801 -0
- package/dist/src/search.js.map +1 -0
- package/dist/src/server-detect.js +70 -0
- package/dist/src/server-detect.js.map +1 -0
- package/dist/src/server.js +784 -0
- package/dist/src/server.js.map +1 -0
- package/dist/src/shared.js +309 -0
- package/dist/src/shared.js.map +1 -0
- package/dist/src/sso.js +22 -0
- package/dist/src/sso.js.map +1 -0
- package/dist/src/store.js +1390 -0
- package/dist/src/store.js.map +1 -0
- package/dist/src/tenant.js +17 -0
- package/dist/src/tenant.js.map +1 -0
- package/dist/src/trace.js +64 -0
- package/dist/src/trace.js.map +1 -0
- package/dist/src/working-memory.js +149 -0
- package/dist/src/working-memory.js.map +1 -0
- package/dist/src/yaml.js +98 -0
- package/dist/src/yaml.js.map +1 -0
- package/dist/store.d.ts +9 -1
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +30 -2
- package/dist/store.js.map +1 -1
- package/extensions/openclaw-plugin/openclaw.plugin.json +1 -1
- package/extensions/openclaw-plugin/package.json +1 -1
- package/openclaw.plugin.json +1 -1
- package/package.json +2 -2
- package/dist/import.d.ts +0 -31
- package/dist/import.d.ts.map +0 -1
- package/dist/import.js +0 -307
- package/dist/import.js.map +0 -1
|
@@ -0,0 +1,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,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Config support for Hippo: reads .hippo/config.json with sane defaults.
|
|
3
|
+
*/
|
|
4
|
+
import * as fs from 'fs';
|
|
5
|
+
import * as path from 'path';
|
|
6
|
+
import { DEFAULT_PHYSICS_CONFIG, mergePhysicsConfig } from './physics-config.js';
|
|
7
|
+
const DEFAULT_CONFIG = {
|
|
8
|
+
defaultHalfLifeDays: 7,
|
|
9
|
+
defaultBudget: 4000,
|
|
10
|
+
defaultContextBudget: 3000,
|
|
11
|
+
decayBasis: 'adaptive',
|
|
12
|
+
autoLearnOnSleep: true,
|
|
13
|
+
autoShareOnSleep: true,
|
|
14
|
+
autoSleep: {
|
|
15
|
+
enabled: true,
|
|
16
|
+
threshold: 50,
|
|
17
|
+
},
|
|
18
|
+
embeddings: {
|
|
19
|
+
enabled: 'auto',
|
|
20
|
+
model: 'Xenova/all-MiniLM-L6-v2',
|
|
21
|
+
hybridWeight: 0.6,
|
|
22
|
+
},
|
|
23
|
+
global: {
|
|
24
|
+
enabled: true,
|
|
25
|
+
},
|
|
26
|
+
gitLearnPatterns: [
|
|
27
|
+
'fix', 'revert', 'bug', 'error', 'hotfix', 'bugfix',
|
|
28
|
+
'refactor', 'perf', 'chore', 'breaking', 'deprecate',
|
|
29
|
+
],
|
|
30
|
+
physics: { ...DEFAULT_PHYSICS_CONFIG },
|
|
31
|
+
mmr: {
|
|
32
|
+
enabled: true,
|
|
33
|
+
lambda: 0.7,
|
|
34
|
+
},
|
|
35
|
+
search: {
|
|
36
|
+
localBump: 1.2,
|
|
37
|
+
},
|
|
38
|
+
replay: {
|
|
39
|
+
count: 5,
|
|
40
|
+
},
|
|
41
|
+
autoTraceCapture: true,
|
|
42
|
+
autoTraceWindowDays: 7,
|
|
43
|
+
pinnedInject: {
|
|
44
|
+
enabled: true,
|
|
45
|
+
budget: 1500,
|
|
46
|
+
},
|
|
47
|
+
extraction: {
|
|
48
|
+
enabled: 'auto',
|
|
49
|
+
model: 'claude-sonnet-4-6',
|
|
50
|
+
},
|
|
51
|
+
multihop: {
|
|
52
|
+
enabled: false,
|
|
53
|
+
},
|
|
54
|
+
salience: {
|
|
55
|
+
enabled: false,
|
|
56
|
+
recentWindow: 20,
|
|
57
|
+
overlapThreshold: 0.6,
|
|
58
|
+
minContentLength: 5,
|
|
59
|
+
maxRepeatErrors: 4,
|
|
60
|
+
},
|
|
61
|
+
ambient: {
|
|
62
|
+
enabled: true,
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
export function loadConfig(hippoRoot) {
|
|
66
|
+
const configPath = path.join(hippoRoot, 'config.json');
|
|
67
|
+
if (!fs.existsSync(configPath))
|
|
68
|
+
return { ...DEFAULT_CONFIG };
|
|
69
|
+
try {
|
|
70
|
+
const raw = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
71
|
+
const basis = raw.decayBasis;
|
|
72
|
+
const validBasis = basis === 'clock' || basis === 'session' || basis === 'adaptive';
|
|
73
|
+
return {
|
|
74
|
+
defaultHalfLifeDays: raw.defaultHalfLifeDays ?? DEFAULT_CONFIG.defaultHalfLifeDays,
|
|
75
|
+
defaultBudget: raw.defaultBudget ?? DEFAULT_CONFIG.defaultBudget,
|
|
76
|
+
defaultContextBudget: raw.defaultContextBudget ?? DEFAULT_CONFIG.defaultContextBudget,
|
|
77
|
+
decayBasis: validBasis ? basis : DEFAULT_CONFIG.decayBasis,
|
|
78
|
+
autoLearnOnSleep: raw.autoLearnOnSleep ?? DEFAULT_CONFIG.autoLearnOnSleep,
|
|
79
|
+
autoShareOnSleep: raw.autoShareOnSleep ?? DEFAULT_CONFIG.autoShareOnSleep,
|
|
80
|
+
autoSleep: { ...DEFAULT_CONFIG.autoSleep, ...(raw.autoSleep ?? {}) },
|
|
81
|
+
embeddings: { ...DEFAULT_CONFIG.embeddings, ...(raw.embeddings ?? {}) },
|
|
82
|
+
global: { ...DEFAULT_CONFIG.global, ...(raw.global ?? {}) },
|
|
83
|
+
gitLearnPatterns: raw.gitLearnPatterns ?? DEFAULT_CONFIG.gitLearnPatterns,
|
|
84
|
+
physics: mergePhysicsConfig(raw.physics),
|
|
85
|
+
mmr: { ...DEFAULT_CONFIG.mmr, ...(raw.mmr ?? {}) },
|
|
86
|
+
search: { ...DEFAULT_CONFIG.search, ...(raw.search ?? {}) },
|
|
87
|
+
replay: { ...DEFAULT_CONFIG.replay, ...(raw.replay ?? {}) },
|
|
88
|
+
autoTraceCapture: raw.autoTraceCapture ?? DEFAULT_CONFIG.autoTraceCapture,
|
|
89
|
+
autoTraceWindowDays: raw.autoTraceWindowDays ?? DEFAULT_CONFIG.autoTraceWindowDays,
|
|
90
|
+
pinnedInject: { ...DEFAULT_CONFIG.pinnedInject, ...(raw.pinnedInject ?? {}) },
|
|
91
|
+
extraction: { ...DEFAULT_CONFIG.extraction, ...(raw.extraction ?? {}) },
|
|
92
|
+
multihop: { ...DEFAULT_CONFIG.multihop, ...(raw.multihop ?? {}) },
|
|
93
|
+
salience: { ...DEFAULT_CONFIG.salience, ...(raw.salience ?? {}) },
|
|
94
|
+
ambient: { ...DEFAULT_CONFIG.ambient, ...(raw.ambient ?? {}) },
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
catch (err) {
|
|
98
|
+
if (fs.existsSync(configPath)) {
|
|
99
|
+
console.error(`Warning: failed to parse ${configPath}: ${err instanceof Error ? err.message : err}`);
|
|
100
|
+
}
|
|
101
|
+
return { ...DEFAULT_CONFIG };
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
export function saveConfig(hippoRoot, config) {
|
|
105
|
+
const configPath = path.join(hippoRoot, 'config.json');
|
|
106
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2), 'utf8');
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -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,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,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,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,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,18 @@
|
|
|
1
|
+
export async function fetchWithRetry(opts) {
|
|
2
|
+
const f = opts.fetchImpl ?? fetch;
|
|
3
|
+
const sleep = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
4
|
+
const max = opts.maxRetries ?? 3;
|
|
5
|
+
let attempt = 0;
|
|
6
|
+
while (true) {
|
|
7
|
+
const r = await f(opts.url, opts.init);
|
|
8
|
+
if (r.status !== 429)
|
|
9
|
+
return r;
|
|
10
|
+
if (attempt >= max)
|
|
11
|
+
throw new Error(`rate-limited after ${attempt + 1} attempts: ${opts.url}`);
|
|
12
|
+
const ra = r.headers.get('retry-after');
|
|
13
|
+
const delaySec = ra ? Number(ra) : Math.pow(2, attempt);
|
|
14
|
+
await sleep(Math.max(0, delaySec) * 1000);
|
|
15
|
+
attempt++;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=ratelimit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ratelimit.js","sourceRoot":"","sources":["../../../../src/connectors/slack/ratelimit.ts"],"names":[],"mappings":"AAQA,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,IAAe;IAClD,MAAM,CAAC,GAAG,IAAI,CAAC,SAAS,IAAI,KAAK,CAAC;IAClC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,CAAC,CAAC,EAAU,EAAE,EAAE,CAAC,IAAI,OAAO,CAAO,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;IAC1F,MAAM,GAAG,GAAG,IAAI,CAAC,UAAU,IAAI,CAAC,CAAC;IACjC,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,OAAO,IAAI,EAAE,CAAC;QACZ,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;QACvC,IAAI,CAAC,CAAC,MAAM,KAAK,GAAG;YAAE,OAAO,CAAC,CAAC;QAC/B,IAAI,OAAO,IAAI,GAAG;YAAE,MAAM,IAAI,KAAK,CAAC,sBAAsB,OAAO,GAAG,CAAC,cAAc,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;QAC/F,MAAM,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;QACxC,MAAM,QAAQ,GAAG,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;QACxD,MAAM,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,QAAQ,CAAC,GAAG,IAAI,CAAC,CAAC;QAC1C,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Map a Slack channel into a hippo scope string.
|
|
3
|
+
*
|
|
4
|
+
* Default to private when privacy is undetermined. The cost of leaking a
|
|
5
|
+
* public channel into private scope (recall returns nothing) is far smaller
|
|
6
|
+
* than the cost of leaking a private channel into public scope (data exposed
|
|
7
|
+
* to a tenant that should not see it).
|
|
8
|
+
*/
|
|
9
|
+
export function scopeFromChannel(ch) {
|
|
10
|
+
const isPublic = ch.is_private === false && !ch.is_im && !ch.is_mpim;
|
|
11
|
+
return isPublic ? `slack:public:${ch.id}` : `slack:private:${ch.id}`;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=scope.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scope.js","sourceRoot":"","sources":["../../../../src/connectors/slack/scope.ts"],"names":[],"mappings":"AAOA;;;;;;;GAOG;AACH,MAAM,UAAU,gBAAgB,CAAC,EAAe;IAC9C,MAAM,QAAQ,GAAG,EAAE,CAAC,UAAU,KAAK,KAAK,IAAI,CAAC,EAAE,CAAC,KAAK,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC;IACrE,OAAO,QAAQ,CAAC,CAAC,CAAC,gBAAgB,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,iBAAiB,EAAE,CAAC,EAAE,EAAE,CAAC;AACvE,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { createHmac, timingSafeEqual } from 'crypto';
|
|
2
|
+
export function verifySlackSignature(opts) {
|
|
3
|
+
const { rawBody, timestamp, signature, signingSecret } = opts;
|
|
4
|
+
const now = opts.now ?? Math.floor(Date.now() / 1000);
|
|
5
|
+
const skew = opts.skewSeconds ?? 5 * 60;
|
|
6
|
+
const ts = Number(timestamp);
|
|
7
|
+
if (!Number.isFinite(ts))
|
|
8
|
+
return false;
|
|
9
|
+
if (Math.abs(now - ts) > skew)
|
|
10
|
+
return false;
|
|
11
|
+
if (!signature.startsWith('v0='))
|
|
12
|
+
return false;
|
|
13
|
+
const expected = `v0=${createHmac('sha256', signingSecret).update(`v0:${timestamp}:${rawBody}`).digest('hex')}`;
|
|
14
|
+
const a = Buffer.from(signature, 'utf8');
|
|
15
|
+
const b = Buffer.from(expected, 'utf8');
|
|
16
|
+
if (a.length !== b.length)
|
|
17
|
+
return false;
|
|
18
|
+
return timingSafeEqual(a, b);
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=signature.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signature.js","sourceRoot":"","sources":["../../../../src/connectors/slack/signature.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,QAAQ,CAAC;AAarD,MAAM,UAAU,oBAAoB,CAAC,IAAgB;IACnD,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,aAAa,EAAE,GAAG,IAAI,CAAC;IAC9D,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;IACtD,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,IAAI,CAAC,GAAG,EAAE,CAAC;IACxC,MAAM,EAAE,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC;IAC7B,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;QAAE,OAAO,KAAK,CAAC;IACvC,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,EAAE,CAAC,GAAG,IAAI;QAAE,OAAO,KAAK,CAAC;IAC5C,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IAC/C,MAAM,QAAQ,GAAG,MAAM,UAAU,CAAC,QAAQ,EAAE,aAAa,CAAC,CAAC,MAAM,CAAC,MAAM,SAAS,IAAI,OAAO,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;IAChH,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;IACzC,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IACxC,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IACxC,OAAO,eAAe,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AAC/B,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Look up the tenant_id for a Slack team_id. Returns null when no row exists,
|
|
3
|
+
* which signals "use the deployment's HIPPO_TENANT fallback". Multi-workspace
|
|
4
|
+
* deployments populate slack_workspaces; single-workspace deployments leave
|
|
5
|
+
* the table empty and rely on the env fallback.
|
|
6
|
+
*
|
|
7
|
+
* Review patch #6: dedicated routing seam — never inline this lookup at the
|
|
8
|
+
* route handler so a future schema change (e.g. installation tokens) lands
|
|
9
|
+
* in one place.
|
|
10
|
+
*/
|
|
11
|
+
export function resolveTenantForTeam(db, teamId) {
|
|
12
|
+
const row = db
|
|
13
|
+
.prepare(`SELECT tenant_id FROM slack_workspaces WHERE team_id = ?`)
|
|
14
|
+
.get(teamId);
|
|
15
|
+
return row?.tenant_id ?? null;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=tenant-routing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tenant-routing.js","sourceRoot":"","sources":["../../../../src/connectors/slack/tenant-routing.ts"],"names":[],"mappings":"AAEA;;;;;;;;;GASG;AACH,MAAM,UAAU,oBAAoB,CAAC,EAAoB,EAAE,MAAc;IACvE,MAAM,GAAG,GAAG,EAAE;SACX,OAAO,CAAC,0DAA0D,CAAC;SACnE,GAAG,CAAC,MAAM,CAAuC,CAAC;IACrD,OAAO,GAAG,EAAE,SAAS,IAAI,IAAI,CAAC;AAChC,CAAC"}
|