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.
- package/README.md +16 -0
- 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 +82 -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 +46 -1
- package/dist/db.js.map +1 -1
- package/dist/importers.js +3 -3
- package/dist/importers.js.map +1 -1
- package/dist/mcp/server.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 +4971 -0
- package/dist/src/cli.js.map +1 -0
- package/dist/src/client.js +181 -0
- package/dist/src/client.js.map +1 -0
- package/dist/src/config.js +108 -0
- package/dist/src/config.js.map +1 -0
- package/dist/src/connectors/slack/backfill.js +76 -0
- package/dist/src/connectors/slack/backfill.js.map +1 -0
- package/dist/src/connectors/slack/deletion.js +46 -0
- package/dist/src/connectors/slack/deletion.js.map +1 -0
- package/dist/src/connectors/slack/dlq.js +23 -0
- package/dist/src/connectors/slack/dlq.js.map +1 -0
- package/dist/src/connectors/slack/idempotency.js +13 -0
- package/dist/src/connectors/slack/idempotency.js.map +1 -0
- package/dist/src/connectors/slack/ingest.js +48 -0
- package/dist/src/connectors/slack/ingest.js.map +1 -0
- package/dist/src/connectors/slack/ratelimit.js +18 -0
- package/dist/src/connectors/slack/ratelimit.js.map +1 -0
- package/dist/src/connectors/slack/scope.js +13 -0
- package/dist/src/connectors/slack/scope.js.map +1 -0
- package/dist/src/connectors/slack/signature.js +20 -0
- package/dist/src/connectors/slack/signature.js.map +1 -0
- package/dist/src/connectors/slack/tenant-routing.js +17 -0
- package/dist/src/connectors/slack/tenant-routing.js.map +1 -0
- package/dist/src/connectors/slack/transform.js +31 -0
- package/dist/src/connectors/slack/transform.js.map +1 -0
- package/dist/src/connectors/slack/types.js +23 -0
- package/dist/src/connectors/slack/types.js.map +1 -0
- package/dist/src/connectors/slack/web-client.js +43 -0
- package/dist/src/connectors/slack/web-client.js.map +1 -0
- package/dist/src/consolidate.js +517 -0
- package/dist/src/consolidate.js.map +1 -0
- package/dist/src/dag.js +104 -0
- package/dist/src/dag.js.map +1 -0
- package/dist/src/dashboard.js +409 -0
- package/dist/src/dashboard.js.map +1 -0
- package/dist/src/db.js +584 -0
- package/dist/src/db.js.map +1 -0
- package/dist/src/embeddings.js +344 -0
- package/dist/src/embeddings.js.map +1 -0
- package/dist/src/eval-suite.js +289 -0
- package/dist/src/eval-suite.js.map +1 -0
- package/dist/src/eval.js +187 -0
- package/dist/src/eval.js.map +1 -0
- package/dist/src/extract.js +87 -0
- package/dist/src/extract.js.map +1 -0
- package/dist/src/handoff.js +30 -0
- package/dist/src/handoff.js.map +1 -0
- package/dist/src/hooks.js +582 -0
- package/dist/src/hooks.js.map +1 -0
- package/dist/src/importers.js +399 -0
- package/dist/src/importers.js.map +1 -0
- package/dist/src/index.js +25 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/invalidation.js +94 -0
- package/dist/src/invalidation.js.map +1 -0
- package/dist/src/mcp/framing.js +45 -0
- package/dist/src/mcp/framing.js.map +1 -0
- package/dist/src/mcp/server.js +510 -0
- package/dist/src/mcp/server.js.map +1 -0
- package/dist/src/memory.js +280 -0
- package/dist/src/memory.js.map +1 -0
- package/dist/src/multihop.js +32 -0
- package/dist/src/multihop.js.map +1 -0
- package/dist/src/path-context.js +32 -0
- package/dist/src/path-context.js.map +1 -0
- package/dist/src/physics-config.js +26 -0
- package/dist/src/physics-config.js.map +1 -0
- package/dist/src/physics-state.js +163 -0
- package/dist/src/physics-state.js.map +1 -0
- package/dist/src/physics.js +361 -0
- package/dist/src/physics.js.map +1 -0
- package/dist/src/postinstall.js +68 -0
- package/dist/src/postinstall.js.map +1 -0
- package/dist/src/raw-archive.js +72 -0
- package/dist/src/raw-archive.js.map +1 -0
- package/dist/src/refine-llm.js +147 -0
- package/dist/src/refine-llm.js.map +1 -0
- package/dist/src/replay.js +117 -0
- package/dist/src/replay.js.map +1 -0
- package/dist/src/salience.js +74 -0
- package/dist/src/salience.js.map +1 -0
- package/dist/src/scheduler.js +67 -0
- package/dist/src/scheduler.js.map +1 -0
- package/dist/src/scope.js +35 -0
- package/dist/src/scope.js.map +1 -0
- package/dist/src/search.js +801 -0
- package/dist/src/search.js.map +1 -0
- package/dist/src/server-detect.js +70 -0
- package/dist/src/server-detect.js.map +1 -0
- package/dist/src/server.js +784 -0
- package/dist/src/server.js.map +1 -0
- package/dist/src/shared.js +309 -0
- package/dist/src/shared.js.map +1 -0
- package/dist/src/sso.js +22 -0
- package/dist/src/sso.js.map +1 -0
- package/dist/src/store.js +1390 -0
- package/dist/src/store.js.map +1 -0
- package/dist/src/tenant.js +17 -0
- package/dist/src/tenant.js.map +1 -0
- package/dist/src/trace.js +64 -0
- package/dist/src/trace.js.map +1 -0
- package/dist/src/working-memory.js +149 -0
- package/dist/src/working-memory.js.map +1 -0
- package/dist/src/yaml.js +98 -0
- package/dist/src/yaml.js.map +1 -0
- package/dist/store.d.ts +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,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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { scopeFromChannel } from './scope.js';
|
|
2
|
+
/**
|
|
3
|
+
* Convert a SlackMessageEvent into RememberOpts for api.remember(). Returns
|
|
4
|
+
* null when the message has no usable body (e.g. system events, bot reactions
|
|
5
|
+
* without text). Caller treats null as "skip but mark idempotency seen".
|
|
6
|
+
*
|
|
7
|
+
* Contract:
|
|
8
|
+
* - kind is the literal 'raw' (E1.x connector boundary, see src/importers.ts).
|
|
9
|
+
* - artifact_ref format MUST be exactly `slack://${teamId}/${channelId}/${ts}`;
|
|
10
|
+
* the deletion path (Task 9) looks up by this string.
|
|
11
|
+
*/
|
|
12
|
+
export function messageToRememberOpts(input) {
|
|
13
|
+
const text = input.message.text?.trim();
|
|
14
|
+
if (!text)
|
|
15
|
+
return null;
|
|
16
|
+
const artifactRef = `slack://${input.teamId}/${input.channel.id}/${input.message.ts}`;
|
|
17
|
+
const tags = [
|
|
18
|
+
'source:slack',
|
|
19
|
+
`channel:${input.channel.id}`,
|
|
20
|
+
...(input.message.user ? [`user:${input.message.user}`] : []),
|
|
21
|
+
...(input.message.thread_ts ? [`thread:${input.message.thread_ts}`] : []),
|
|
22
|
+
];
|
|
23
|
+
return {
|
|
24
|
+
content: text,
|
|
25
|
+
kind: 'raw',
|
|
26
|
+
scope: scopeFromChannel(input.channel),
|
|
27
|
+
artifactRef,
|
|
28
|
+
tags,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=transform.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transform.js","sourceRoot":"","sources":["../../../../src/connectors/slack/transform.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAoB,MAAM,YAAY,CAAC;AAShE;;;;;;;;;GASG;AACH,MAAM,UAAU,qBAAqB,CAAC,KAAqB;IACzD,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC;IACxC,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC;IACvB,MAAM,WAAW,GAAG,WAAW,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,EAAE,IAAI,KAAK,CAAC,OAAO,CAAC,EAAE,EAAE,CAAC;IACtF,MAAM,IAAI,GAAG;QACX,cAAc;QACd,WAAW,KAAK,CAAC,OAAO,CAAC,EAAE,EAAE;QAC7B,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,QAAQ,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAC7D,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,UAAU,KAAK,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;KAC1E,CAAC;IACF,OAAO;QACL,OAAO,EAAE,IAAI;QACb,IAAI,EAAE,KAAK;QACX,KAAK,EAAE,gBAAgB,CAAC,KAAK,CAAC,OAAO,CAAC;QACtC,WAAW;QACX,IAAI;KACL,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Slack Events API envelope shapes used by the ingestion connector.
|
|
3
|
+
* Spec: https://api.slack.com/events-api
|
|
4
|
+
*/
|
|
5
|
+
export function isSlackEventEnvelope(x) {
|
|
6
|
+
if (!x || typeof x !== 'object')
|
|
7
|
+
return false;
|
|
8
|
+
const o = x;
|
|
9
|
+
return (o.type === 'event_callback' &&
|
|
10
|
+
typeof o.team_id === 'string' &&
|
|
11
|
+
typeof o.event_id === 'string' &&
|
|
12
|
+
typeof o.event_time === 'number' &&
|
|
13
|
+
!!o.event &&
|
|
14
|
+
typeof o.event === 'object' &&
|
|
15
|
+
typeof o.event.type === 'string');
|
|
16
|
+
}
|
|
17
|
+
export function isSlackMessageEvent(x) {
|
|
18
|
+
if (!x || typeof x !== 'object')
|
|
19
|
+
return false;
|
|
20
|
+
const o = x;
|
|
21
|
+
return o.type === 'message' && typeof o.channel === 'string' && typeof o.ts === 'string';
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../src/connectors/slack/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AA+BH,MAAM,UAAU,oBAAoB,CAAC,CAAU;IAC7C,IAAI,CAAC,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC9C,MAAM,CAAC,GAAG,CAA4B,CAAC;IACvC,OAAO,CACL,CAAC,CAAC,IAAI,KAAK,gBAAgB;QAC3B,OAAO,CAAC,CAAC,OAAO,KAAK,QAAQ;QAC7B,OAAO,CAAC,CAAC,QAAQ,KAAK,QAAQ;QAC9B,OAAO,CAAC,CAAC,UAAU,KAAK,QAAQ;QAChC,CAAC,CAAC,CAAC,CAAC,KAAK;QACT,OAAO,CAAC,CAAC,KAAK,KAAK,QAAQ;QAC3B,OAAQ,CAAC,CAAC,KAAiC,CAAC,IAAI,KAAK,QAAQ,CAC9D,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,CAAU;IAC5C,IAAI,CAAC,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC9C,MAAM,CAAC,GAAG,CAA4B,CAAC;IACvC,OAAO,CAAC,CAAC,IAAI,KAAK,SAAS,IAAI,OAAO,CAAC,CAAC,OAAO,KAAK,QAAQ,IAAI,OAAO,CAAC,CAAC,EAAE,KAAK,QAAQ,CAAC;AAC3F,CAAC"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { fetchWithRetry } from './ratelimit.js';
|
|
2
|
+
/**
|
|
3
|
+
* Build a SlackHistoryFetcher that pages `conversations.history` over real
|
|
4
|
+
* HTTP. Wraps `fetchWithRetry` so 429 handling is automatic. The returned
|
|
5
|
+
* fetcher is the one Task 13's `backfillChannel` consumes.
|
|
6
|
+
*
|
|
7
|
+
* Slack omits `channel` from messages in the history response, so we stamp
|
|
8
|
+
* the request channel id onto each parsed message — downstream ingest needs
|
|
9
|
+
* it on every event.
|
|
10
|
+
*/
|
|
11
|
+
export function slackHistoryFetcher(token, fetchImpl) {
|
|
12
|
+
return async ({ channelId, cursor, oldest }) => {
|
|
13
|
+
const url = new URL('https://slack.com/api/conversations.history');
|
|
14
|
+
url.searchParams.set('channel', channelId);
|
|
15
|
+
url.searchParams.set('limit', '200');
|
|
16
|
+
if (cursor)
|
|
17
|
+
url.searchParams.set('cursor', cursor);
|
|
18
|
+
if (oldest)
|
|
19
|
+
url.searchParams.set('oldest', oldest);
|
|
20
|
+
const r = await fetchWithRetry({
|
|
21
|
+
url: url.toString(),
|
|
22
|
+
init: { method: 'GET', headers: { authorization: `Bearer ${token}` } },
|
|
23
|
+
fetchImpl,
|
|
24
|
+
});
|
|
25
|
+
const body = (await r.json());
|
|
26
|
+
if (!body.ok)
|
|
27
|
+
throw new Error(`slack: ${body.error ?? 'unknown error'}`);
|
|
28
|
+
const messages = (body.messages ?? [])
|
|
29
|
+
.filter((m) => {
|
|
30
|
+
if (!m || typeof m !== 'object')
|
|
31
|
+
return false;
|
|
32
|
+
const o = m;
|
|
33
|
+
return o.type === 'message' && typeof o.ts === 'string';
|
|
34
|
+
})
|
|
35
|
+
// Slack returns messages without `channel`; stamp it from the request.
|
|
36
|
+
.map((m) => ({ ...m, channel: channelId }));
|
|
37
|
+
return {
|
|
38
|
+
messages,
|
|
39
|
+
next_cursor: body.response_metadata?.next_cursor ?? null,
|
|
40
|
+
};
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=web-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"web-client.js","sourceRoot":"","sources":["../../../../src/connectors/slack/web-client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAIhD;;;;;;;;GAQG;AACH,MAAM,UAAU,mBAAmB,CACjC,KAAa,EACb,SAAwB;IAExB,OAAO,KAAK,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE;QAC7C,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,6CAA6C,CAAC,CAAC;QACnE,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;QAC3C,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QACrC,IAAI,MAAM;YAAE,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QACnD,IAAI,MAAM;YAAE,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QACnD,MAAM,CAAC,GAAG,MAAM,cAAc,CAAC;YAC7B,GAAG,EAAE,GAAG,CAAC,QAAQ,EAAE;YACnB,IAAI,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,aAAa,EAAE,UAAU,KAAK,EAAE,EAAE,EAAE;YACtE,SAAS;SACV,CAAC,CAAC;QACH,MAAM,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAK3B,CAAC;QACF,IAAI,CAAC,IAAI,CAAC,EAAE;YAAE,MAAM,IAAI,KAAK,CAAC,UAAU,IAAI,CAAC,KAAK,IAAI,eAAe,EAAE,CAAC,CAAC;QACzE,MAAM,QAAQ,GAAwB,CAAC,IAAI,CAAC,QAAQ,IAAI,EAAE,CAAC;aACxD,MAAM,CAAC,CAAC,CAAC,EAA0B,EAAE;YACpC,IAAI,CAAC,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ;gBAAE,OAAO,KAAK,CAAC;YAC9C,MAAM,CAAC,GAAG,CAA4B,CAAC;YACvC,OAAO,CAAC,CAAC,IAAI,KAAK,SAAS,IAAI,OAAO,CAAC,CAAC,EAAE,KAAK,QAAQ,CAAC;QAC1D,CAAC,CAAC;YACF,uEAAuE;aACtE,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC;QAC9C,OAAO;YACL,QAAQ;YACR,WAAW,EAAE,IAAI,CAAC,iBAAiB,EAAE,WAAW,IAAI,IAAI;SACzD,CAAC;IACJ,CAAC,CAAC;AACJ,CAAC"}
|