instar 0.24.13 → 0.24.14
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/.claude/skills/setup-wizard/skill.md +281 -5
- package/dashboard/index.html +341 -0
- package/dist/cli.js +18 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/server.d.ts.map +1 -1
- package/dist/commands/server.js +188 -1
- package/dist/commands/server.js.map +1 -1
- package/dist/commands/slack-cli.d.ts +16 -0
- package/dist/commands/slack-cli.d.ts.map +1 -0
- package/dist/commands/slack-cli.js +198 -0
- package/dist/commands/slack-cli.js.map +1 -0
- package/dist/core/AgentRegistry.d.ts.map +1 -1
- package/dist/core/AgentRegistry.js +24 -6
- package/dist/core/AgentRegistry.js.map +1 -1
- package/dist/core/SleepWakeDetector.d.ts +11 -0
- package/dist/core/SleepWakeDetector.d.ts.map +1 -1
- package/dist/core/SleepWakeDetector.js +16 -1
- package/dist/core/SleepWakeDetector.js.map +1 -1
- package/dist/lifeline/ServerSupervisor.d.ts +13 -0
- package/dist/lifeline/ServerSupervisor.d.ts.map +1 -1
- package/dist/lifeline/ServerSupervisor.js +129 -0
- package/dist/lifeline/ServerSupervisor.js.map +1 -1
- package/dist/messaging/SessionSummarySentinel.js +1 -1
- package/dist/messaging/TelegramAdapter.d.ts +1 -0
- package/dist/messaging/TelegramAdapter.d.ts.map +1 -1
- package/dist/messaging/TelegramAdapter.js +4 -1
- package/dist/messaging/TelegramAdapter.js.map +1 -1
- package/dist/messaging/slack/ChannelManager.d.ts +36 -0
- package/dist/messaging/slack/ChannelManager.d.ts.map +1 -0
- package/dist/messaging/slack/ChannelManager.js +100 -0
- package/dist/messaging/slack/ChannelManager.js.map +1 -0
- package/dist/messaging/slack/FileHandler.d.ts +30 -0
- package/dist/messaging/slack/FileHandler.d.ts.map +1 -0
- package/dist/messaging/slack/FileHandler.js +87 -0
- package/dist/messaging/slack/FileHandler.js.map +1 -0
- package/dist/messaging/slack/RingBuffer.d.ts +22 -0
- package/dist/messaging/slack/RingBuffer.d.ts.map +1 -0
- package/dist/messaging/slack/RingBuffer.js +48 -0
- package/dist/messaging/slack/RingBuffer.js.map +1 -0
- package/dist/messaging/slack/SlackAdapter.d.ts +136 -0
- package/dist/messaging/slack/SlackAdapter.d.ts.map +1 -0
- package/dist/messaging/slack/SlackAdapter.js +572 -0
- package/dist/messaging/slack/SlackAdapter.js.map +1 -0
- package/dist/messaging/slack/SlackApiClient.d.ts +51 -0
- package/dist/messaging/slack/SlackApiClient.d.ts.map +1 -0
- package/dist/messaging/slack/SlackApiClient.js +94 -0
- package/dist/messaging/slack/SlackApiClient.js.map +1 -0
- package/dist/messaging/slack/SocketModeClient.d.ts +44 -0
- package/dist/messaging/slack/SocketModeClient.d.ts.map +1 -0
- package/dist/messaging/slack/SocketModeClient.js +209 -0
- package/dist/messaging/slack/SocketModeClient.js.map +1 -0
- package/dist/messaging/slack/index.d.ts +12 -0
- package/dist/messaging/slack/index.d.ts.map +1 -0
- package/dist/messaging/slack/index.js +15 -0
- package/dist/messaging/slack/index.js.map +1 -0
- package/dist/messaging/slack/sanitize.d.ts +39 -0
- package/dist/messaging/slack/sanitize.d.ts.map +1 -0
- package/dist/messaging/slack/sanitize.js +71 -0
- package/dist/messaging/slack/sanitize.js.map +1 -0
- package/dist/messaging/slack/types.d.ts +155 -0
- package/dist/messaging/slack/types.d.ts.map +1 -0
- package/dist/messaging/slack/types.js +54 -0
- package/dist/messaging/slack/types.js.map +1 -0
- package/dist/monitoring/PresenceProxy.d.ts +157 -0
- package/dist/monitoring/PresenceProxy.d.ts.map +1 -0
- package/dist/monitoring/PresenceProxy.js +891 -0
- package/dist/monitoring/PresenceProxy.js.map +1 -0
- package/dist/monitoring/SessionWatchdog.d.ts.map +1 -1
- package/dist/monitoring/SessionWatchdog.js +2 -0
- package/dist/monitoring/SessionWatchdog.js.map +1 -1
- package/dist/server/AgentServer.d.ts +1 -0
- package/dist/server/AgentServer.d.ts.map +1 -1
- package/dist/server/AgentServer.js +49 -47
- package/dist/server/AgentServer.js.map +1 -1
- package/dist/server/routes.d.ts +1 -0
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +213 -4
- package/dist/server/routes.js.map +1 -1
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +94 -78
- package/src/templates/hooks/slack-channel-context.sh +98 -0
- package/src/templates/scripts/slack-reply.sh +64 -0
- package/upgrades/0.24.11.md +23 -0
- package/upgrades/0.24.14.md +26 -0
- package/upgrades/0.24.6.md +20 -0
- package/upgrades/0.24.7.md +24 -0
- package/upgrades/0.24.8.md +19 -0
- package/upgrades/0.24.9.md +19 -0
- package/upgrades/NEXT.md +35 -0
- /package/.claude/skills/secret-setup/{skill.md → SKILL.md} +0 -0
|
@@ -0,0 +1,572 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SlackAdapter — Native Slack messaging adapter for Instar.
|
|
3
|
+
*
|
|
4
|
+
* Implements the MessagingAdapter interface using Socket Mode (WebSocket)
|
|
5
|
+
* for event intake and the Slack Web API for outbound messages.
|
|
6
|
+
*
|
|
7
|
+
* Key design decisions:
|
|
8
|
+
* - DIY app model (each user creates their own Slack app)
|
|
9
|
+
* - Socket Mode (no public URLs, no webhooks)
|
|
10
|
+
* - Zero external SDK (direct HTTP to Slack Web API)
|
|
11
|
+
* - authorizedUserIds is required and fail-closed
|
|
12
|
+
* - Ring buffer scoped to authorized users only
|
|
13
|
+
* - JSON-encoded context files (no delimiter-based injection)
|
|
14
|
+
*/
|
|
15
|
+
import path from 'node:path';
|
|
16
|
+
import fs from 'node:fs';
|
|
17
|
+
import { SlackApiClient } from './SlackApiClient.js';
|
|
18
|
+
import { SocketModeClient } from './SocketModeClient.js';
|
|
19
|
+
import { ChannelManager } from './ChannelManager.js';
|
|
20
|
+
import { FileHandler } from './FileHandler.js';
|
|
21
|
+
import { RingBuffer } from './RingBuffer.js';
|
|
22
|
+
import { MessageLogger } from '../shared/MessageLogger.js';
|
|
23
|
+
import { sanitizeDisplayName } from './sanitize.js';
|
|
24
|
+
const RING_BUFFER_CAPACITY = 50;
|
|
25
|
+
const SLACK_MAX_TEXT_LENGTH = 4000;
|
|
26
|
+
const AUTO_ARCHIVE_DAYS = 7;
|
|
27
|
+
const LOG_PURGE_INTERVAL_MS = 24 * 60 * 60 * 1000; // Daily
|
|
28
|
+
export class SlackAdapter {
|
|
29
|
+
platform = 'slack';
|
|
30
|
+
// Config
|
|
31
|
+
config;
|
|
32
|
+
stateDir;
|
|
33
|
+
// Components
|
|
34
|
+
apiClient;
|
|
35
|
+
socketClient = null;
|
|
36
|
+
channelManager;
|
|
37
|
+
fileHandler;
|
|
38
|
+
logger;
|
|
39
|
+
// State
|
|
40
|
+
messageHandler = null;
|
|
41
|
+
started = false;
|
|
42
|
+
authorizedUsers;
|
|
43
|
+
channelHistory = new Map();
|
|
44
|
+
pendingPrompts = new Map();
|
|
45
|
+
userCache = new Map();
|
|
46
|
+
promptEvictionTimer = null;
|
|
47
|
+
housekeepingTimer = null;
|
|
48
|
+
logPurgeTimer = null;
|
|
49
|
+
// Callbacks (wired by server.ts)
|
|
50
|
+
/** Called when a prompt gate response is received */
|
|
51
|
+
onPromptResponse = null;
|
|
52
|
+
/** Called when a message is logged (for dual-write to SQLite) */
|
|
53
|
+
onMessageLogged = null;
|
|
54
|
+
constructor(config, stateDir) {
|
|
55
|
+
this.config = config;
|
|
56
|
+
this.stateDir = stateDir;
|
|
57
|
+
// Validate required fields
|
|
58
|
+
if (!this.config.botToken)
|
|
59
|
+
throw new Error('[slack] botToken is required');
|
|
60
|
+
if (!this.config.appToken)
|
|
61
|
+
throw new Error('[slack] appToken is required');
|
|
62
|
+
if (!Array.isArray(this.config.authorizedUserIds)) {
|
|
63
|
+
throw new Error('[slack] authorizedUserIds is required (array of Slack user IDs)');
|
|
64
|
+
}
|
|
65
|
+
// Fail-closed: empty array means deny all
|
|
66
|
+
this.authorizedUsers = new Set(this.config.authorizedUserIds);
|
|
67
|
+
if (this.authorizedUsers.size === 0) {
|
|
68
|
+
console.warn('[slack] authorizedUserIds is empty — all messages will be rejected (fail-closed)');
|
|
69
|
+
}
|
|
70
|
+
// Initialize components
|
|
71
|
+
this.apiClient = new SlackApiClient(this.config.botToken, this.config.appToken);
|
|
72
|
+
const agentName = this.config.workspaceName?.replace(/-agent$/, '') || 'agent';
|
|
73
|
+
this.channelManager = new ChannelManager(this.apiClient, agentName);
|
|
74
|
+
this.fileHandler = new FileHandler(this.apiClient, this.config.botToken, stateDir);
|
|
75
|
+
this.logger = new MessageLogger({
|
|
76
|
+
logPath: path.join(stateDir, 'slack-messages.jsonl'),
|
|
77
|
+
maxLines: 100_000,
|
|
78
|
+
keepLines: 75_000,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
// ── MessagingAdapter Interface ──
|
|
82
|
+
async start() {
|
|
83
|
+
const handlers = {
|
|
84
|
+
onEvent: async (type, payload) => this._handleEvent(type, payload),
|
|
85
|
+
onInteraction: async (payload) => this._handleInteraction(payload),
|
|
86
|
+
onConnected: () => {
|
|
87
|
+
console.log('[slack] Socket Mode connected');
|
|
88
|
+
this.started = true;
|
|
89
|
+
},
|
|
90
|
+
onDisconnected: (reason) => {
|
|
91
|
+
console.log(`[slack] Disconnected: ${reason}`);
|
|
92
|
+
},
|
|
93
|
+
onError: (err, permanent) => {
|
|
94
|
+
if (permanent) {
|
|
95
|
+
console.error(`[slack] Permanent error: ${err.message}`);
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
console.warn(`[slack] Transient error: ${err.message}`);
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
};
|
|
102
|
+
this.socketClient = new SocketModeClient(this.apiClient, handlers);
|
|
103
|
+
await this.socketClient.connect();
|
|
104
|
+
this.started = true;
|
|
105
|
+
// Start pending prompt TTL eviction
|
|
106
|
+
this._startPromptEviction();
|
|
107
|
+
// Start channel housekeeping (auto-archive idle channels)
|
|
108
|
+
this._startHousekeeping();
|
|
109
|
+
// Start log retention purge (daily)
|
|
110
|
+
this._startLogPurge();
|
|
111
|
+
// Purge stale log entries on startup
|
|
112
|
+
this._purgeOldLogs();
|
|
113
|
+
}
|
|
114
|
+
async stop() {
|
|
115
|
+
this.started = false;
|
|
116
|
+
if (this.promptEvictionTimer) {
|
|
117
|
+
clearInterval(this.promptEvictionTimer);
|
|
118
|
+
this.promptEvictionTimer = null;
|
|
119
|
+
}
|
|
120
|
+
if (this.housekeepingTimer) {
|
|
121
|
+
clearInterval(this.housekeepingTimer);
|
|
122
|
+
this.housekeepingTimer = null;
|
|
123
|
+
}
|
|
124
|
+
if (this.logPurgeTimer) {
|
|
125
|
+
clearInterval(this.logPurgeTimer);
|
|
126
|
+
this.logPurgeTimer = null;
|
|
127
|
+
}
|
|
128
|
+
if (this.socketClient) {
|
|
129
|
+
await this.socketClient.disconnect();
|
|
130
|
+
this.socketClient = null;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
async send(message) {
|
|
134
|
+
const channelId = message.channel?.identifier;
|
|
135
|
+
if (!channelId) {
|
|
136
|
+
console.error('[slack] Cannot send: no channel identifier');
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
// Chunk long messages
|
|
140
|
+
const chunks = this._chunkText(message.content);
|
|
141
|
+
let lastResult = null;
|
|
142
|
+
for (const chunk of chunks) {
|
|
143
|
+
const params = {
|
|
144
|
+
channel: channelId,
|
|
145
|
+
text: chunk,
|
|
146
|
+
};
|
|
147
|
+
// If there's thread_ts in metadata, reply in thread
|
|
148
|
+
if (message.channel?.type === 'slack' && message.threadTs) {
|
|
149
|
+
params.thread_ts = message.threadTs;
|
|
150
|
+
}
|
|
151
|
+
lastResult = await this.apiClient.call('chat.postMessage', params);
|
|
152
|
+
}
|
|
153
|
+
return lastResult;
|
|
154
|
+
}
|
|
155
|
+
onMessage(handler) {
|
|
156
|
+
this.messageHandler = handler;
|
|
157
|
+
}
|
|
158
|
+
async resolveUser(channelIdentifier) {
|
|
159
|
+
// For Slack, the channel identifier IS the user reference
|
|
160
|
+
return channelIdentifier || null;
|
|
161
|
+
}
|
|
162
|
+
// ── Slack-Specific Public Methods ──
|
|
163
|
+
/** Check if a user is authorized. */
|
|
164
|
+
isAuthorized(userId) {
|
|
165
|
+
return this.authorizedUsers.has(userId);
|
|
166
|
+
}
|
|
167
|
+
/** Send a message to a specific channel. */
|
|
168
|
+
async sendToChannel(channelId, text, options) {
|
|
169
|
+
const params = { channel: channelId, text };
|
|
170
|
+
if (options?.thread_ts)
|
|
171
|
+
params.thread_ts = options.thread_ts;
|
|
172
|
+
const result = await this.apiClient.call('chat.postMessage', params);
|
|
173
|
+
return result.ts;
|
|
174
|
+
}
|
|
175
|
+
/** Add a reaction (fire-and-forget). */
|
|
176
|
+
addReaction(channelId, timestamp, emoji) {
|
|
177
|
+
this.apiClient.call('reactions.add', { channel: channelId, timestamp, name: emoji }).catch(() => { });
|
|
178
|
+
}
|
|
179
|
+
/** Remove a reaction (fire-and-forget). */
|
|
180
|
+
removeReaction(channelId, timestamp, emoji) {
|
|
181
|
+
this.apiClient.call('reactions.remove', { channel: channelId, timestamp, name: emoji }).catch(() => { });
|
|
182
|
+
}
|
|
183
|
+
/** Update an existing message. */
|
|
184
|
+
async updateMessage(channelId, timestamp, text) {
|
|
185
|
+
await this.apiClient.call('chat.update', { channel: channelId, ts: timestamp, text });
|
|
186
|
+
}
|
|
187
|
+
/** Pin a message. */
|
|
188
|
+
async pinMessage(channelId, timestamp) {
|
|
189
|
+
await this.apiClient.call('pins.add', { channel: channelId, timestamp });
|
|
190
|
+
}
|
|
191
|
+
/** Send an ephemeral message (visible only to one user). */
|
|
192
|
+
async postEphemeral(channelId, userId, text) {
|
|
193
|
+
await this.apiClient.call('chat.postEphemeral', { channel: channelId, user: userId, text });
|
|
194
|
+
}
|
|
195
|
+
/** Send a message with Block Kit blocks. */
|
|
196
|
+
async sendBlocks(channelId, blocks, text) {
|
|
197
|
+
const params = { channel: channelId, blocks };
|
|
198
|
+
if (text)
|
|
199
|
+
params.text = text; // Fallback text for notifications
|
|
200
|
+
const result = await this.apiClient.call('chat.postMessage', params);
|
|
201
|
+
return result.ts;
|
|
202
|
+
}
|
|
203
|
+
/** Get cached channel messages from ring buffer. */
|
|
204
|
+
getChannelMessages(channelId, limit = 30) {
|
|
205
|
+
const buffer = this.channelHistory.get(channelId);
|
|
206
|
+
if (!buffer)
|
|
207
|
+
return [];
|
|
208
|
+
const all = buffer.toArray();
|
|
209
|
+
return limit >= all.length ? all : all.slice(-limit);
|
|
210
|
+
}
|
|
211
|
+
/** Get user info (cached for 5 minutes). */
|
|
212
|
+
async getUserInfo(userId) {
|
|
213
|
+
const cached = this.userCache.get(userId);
|
|
214
|
+
if (cached && Date.now() - cached.fetchedAt < 5 * 60 * 1000) {
|
|
215
|
+
return { id: userId, name: cached.name };
|
|
216
|
+
}
|
|
217
|
+
const result = await this.apiClient.call('users.info', { user: userId });
|
|
218
|
+
const user = result.user;
|
|
219
|
+
const name = user.real_name || user.name;
|
|
220
|
+
this.userCache.set(userId, { name, fetchedAt: Date.now() });
|
|
221
|
+
return { id: userId, name };
|
|
222
|
+
}
|
|
223
|
+
/** Create a channel. */
|
|
224
|
+
async createChannel(name, isPrivate) {
|
|
225
|
+
return this.channelManager.createChannel(name, isPrivate);
|
|
226
|
+
}
|
|
227
|
+
/** Archive a channel. */
|
|
228
|
+
async archiveChannel(channelId) {
|
|
229
|
+
return this.channelManager.archiveChannel(channelId);
|
|
230
|
+
}
|
|
231
|
+
/** Upload a file. */
|
|
232
|
+
async uploadFile(channelId, filePath, title) {
|
|
233
|
+
return this.fileHandler.uploadFile(channelId, filePath, title);
|
|
234
|
+
}
|
|
235
|
+
/** Download a file. */
|
|
236
|
+
async downloadFile(url, destPath) {
|
|
237
|
+
return this.fileHandler.downloadFile(url, destPath);
|
|
238
|
+
}
|
|
239
|
+
/** Get the underlying API client (for routes). */
|
|
240
|
+
get api() {
|
|
241
|
+
return this.apiClient;
|
|
242
|
+
}
|
|
243
|
+
// ── Test Helpers (underscore-prefixed) ──
|
|
244
|
+
/** Inject a simulated message for testing. */
|
|
245
|
+
async _testInjectMessage(event) {
|
|
246
|
+
await this._handleEvent('message', { event });
|
|
247
|
+
}
|
|
248
|
+
/** Inject a simulated interaction for testing. */
|
|
249
|
+
async _testInjectInteraction(payload) {
|
|
250
|
+
await this._handleInteraction(payload);
|
|
251
|
+
}
|
|
252
|
+
// ── Internal Event Handling ──
|
|
253
|
+
async _handleEvent(type, payload) {
|
|
254
|
+
const event = (payload.event ?? payload);
|
|
255
|
+
if (type === 'message' || event.type === 'message') {
|
|
256
|
+
await this._handleMessage(event);
|
|
257
|
+
}
|
|
258
|
+
else if (type === 'file_shared') {
|
|
259
|
+
await this._handleFileShared(event);
|
|
260
|
+
}
|
|
261
|
+
// reaction_added, app_mention can be handled later
|
|
262
|
+
}
|
|
263
|
+
async _handleMessage(event) {
|
|
264
|
+
const userId = event.user;
|
|
265
|
+
const text = event.text ?? '';
|
|
266
|
+
const channelId = event.channel;
|
|
267
|
+
const ts = event.ts;
|
|
268
|
+
const threadTs = event.thread_ts;
|
|
269
|
+
// Skip bot messages and subtypes (edits, deletes, etc.)
|
|
270
|
+
if (event.bot_id || event.subtype)
|
|
271
|
+
return;
|
|
272
|
+
if (!userId || !channelId)
|
|
273
|
+
return;
|
|
274
|
+
// AuthGate — fail-closed
|
|
275
|
+
if (!this.isAuthorized(userId)) {
|
|
276
|
+
return; // Silently drop unauthorized messages
|
|
277
|
+
}
|
|
278
|
+
// Populate ring buffer (authorized messages only — prevents cache poisoning)
|
|
279
|
+
const buffer = this.channelHistory.get(channelId) ?? new RingBuffer(RING_BUFFER_CAPACITY);
|
|
280
|
+
buffer.push({ ts, user: userId, text, channel: channelId, thread_ts: threadTs });
|
|
281
|
+
this.channelHistory.set(channelId, buffer);
|
|
282
|
+
// Resolve user name
|
|
283
|
+
let senderName = userId;
|
|
284
|
+
try {
|
|
285
|
+
const info = await this.getUserInfo(userId);
|
|
286
|
+
senderName = info.name;
|
|
287
|
+
}
|
|
288
|
+
catch {
|
|
289
|
+
// Use userId as fallback
|
|
290
|
+
}
|
|
291
|
+
// Log inbound message
|
|
292
|
+
const logEntry = {
|
|
293
|
+
messageId: ts,
|
|
294
|
+
channelId,
|
|
295
|
+
text,
|
|
296
|
+
fromUser: true,
|
|
297
|
+
timestamp: new Date(parseFloat(ts) * 1000).toISOString(),
|
|
298
|
+
sessionName: null,
|
|
299
|
+
senderName: sanitizeDisplayName(senderName),
|
|
300
|
+
platformUserId: userId,
|
|
301
|
+
platform: 'slack',
|
|
302
|
+
};
|
|
303
|
+
this.logger.append(logEntry);
|
|
304
|
+
this.onMessageLogged?.(logEntry);
|
|
305
|
+
// Acknowledge with reaction (fire-and-forget)
|
|
306
|
+
this.addReaction(channelId, ts, 'eyes');
|
|
307
|
+
// Convert to Instar Message format
|
|
308
|
+
const message = {
|
|
309
|
+
id: `slack-${ts}`,
|
|
310
|
+
userId,
|
|
311
|
+
content: text,
|
|
312
|
+
channel: {
|
|
313
|
+
type: 'slack',
|
|
314
|
+
identifier: channelId,
|
|
315
|
+
},
|
|
316
|
+
receivedAt: new Date(parseFloat(ts) * 1000).toISOString(),
|
|
317
|
+
metadata: {
|
|
318
|
+
slackUserId: userId,
|
|
319
|
+
senderName: sanitizeDisplayName(senderName),
|
|
320
|
+
ts,
|
|
321
|
+
threadTs: threadTs,
|
|
322
|
+
channelId,
|
|
323
|
+
isDM: channelId.startsWith('D'),
|
|
324
|
+
},
|
|
325
|
+
};
|
|
326
|
+
// Route to handler
|
|
327
|
+
if (this.messageHandler) {
|
|
328
|
+
try {
|
|
329
|
+
await this.messageHandler(message);
|
|
330
|
+
}
|
|
331
|
+
catch (err) {
|
|
332
|
+
console.error('[slack] Message handler error:', err.message);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
// Mark complete (replace eyes with checkmark)
|
|
336
|
+
this.removeReaction(channelId, ts, 'eyes');
|
|
337
|
+
this.addReaction(channelId, ts, 'white_check_mark');
|
|
338
|
+
}
|
|
339
|
+
async _handleInteraction(payload) {
|
|
340
|
+
const userId = payload.user?.id;
|
|
341
|
+
if (!userId)
|
|
342
|
+
return;
|
|
343
|
+
// AuthGate check
|
|
344
|
+
if (!this.isAuthorized(userId)) {
|
|
345
|
+
console.warn(`[slack] Unauthorized interaction from ${userId}`);
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
const action = payload.actions?.[0];
|
|
349
|
+
if (!action)
|
|
350
|
+
return;
|
|
351
|
+
if (action.action_id.startsWith('prompt::')) {
|
|
352
|
+
const parts = action.action_id.split('::');
|
|
353
|
+
const promptId = parts[1];
|
|
354
|
+
// Validate this is a prompt we sent
|
|
355
|
+
const messageTs = payload.message?.ts;
|
|
356
|
+
if (!messageTs || !this.pendingPrompts.has(messageTs)) {
|
|
357
|
+
console.warn(`[slack] Interaction for unknown prompt ts: ${messageTs}`);
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
this.pendingPrompts.delete(messageTs);
|
|
361
|
+
// Update message to show selection
|
|
362
|
+
if (payload.channel?.id && messageTs) {
|
|
363
|
+
await this.updateMessage(payload.channel.id, messageTs, `Answered: ${action.text?.text ?? action.value ?? 'selected'}`).catch(() => { });
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
async _handleFileShared(event) {
|
|
368
|
+
const userId = event.user_id ?? event.user;
|
|
369
|
+
// AuthGate — check before download (prevents disk exhaustion from unauthorized users)
|
|
370
|
+
if (!userId || !this.isAuthorized(userId)) {
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
// File handling would download and route to session
|
|
374
|
+
// Full implementation depends on session injection patterns
|
|
375
|
+
}
|
|
376
|
+
// ── Prompt Gate ──
|
|
377
|
+
/** Register a pending prompt (for interaction validation). */
|
|
378
|
+
registerPendingPrompt(messageTs, promptId, channelId) {
|
|
379
|
+
this.pendingPrompts.set(messageTs, {
|
|
380
|
+
promptId,
|
|
381
|
+
channelId,
|
|
382
|
+
messageTs,
|
|
383
|
+
createdAt: Date.now(),
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
_startPromptEviction() {
|
|
387
|
+
const ttl = (this.config.promptGate?.relayTimeoutSeconds ?? 300) * 1000;
|
|
388
|
+
this.promptEvictionTimer = setInterval(() => {
|
|
389
|
+
const now = Date.now();
|
|
390
|
+
for (const [ts, prompt] of this.pendingPrompts) {
|
|
391
|
+
if (now - prompt.createdAt > ttl) {
|
|
392
|
+
this.pendingPrompts.delete(ts);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
}, 60_000); // Check every 60s
|
|
396
|
+
}
|
|
397
|
+
// ── Utilities ──
|
|
398
|
+
// ── Prompt Gate (Block Kit) ──
|
|
399
|
+
/**
|
|
400
|
+
* Relay a prompt to the user via Block Kit interactive message.
|
|
401
|
+
* Registers the prompt for validation against spoofed button presses.
|
|
402
|
+
*/
|
|
403
|
+
async relayPrompt(channelId, promptId, question, options) {
|
|
404
|
+
const blocks = [
|
|
405
|
+
{
|
|
406
|
+
type: 'section',
|
|
407
|
+
text: { type: 'mrkdwn', text: `*Agent needs your input:*\n${question}` },
|
|
408
|
+
},
|
|
409
|
+
{
|
|
410
|
+
type: 'actions',
|
|
411
|
+
elements: options.map((opt, i) => ({
|
|
412
|
+
type: 'button',
|
|
413
|
+
text: { type: 'plain_text', text: opt.label },
|
|
414
|
+
value: opt.value,
|
|
415
|
+
action_id: `prompt::${promptId}::${i}`,
|
|
416
|
+
...(opt.primary ? { style: 'primary' } : {}),
|
|
417
|
+
})),
|
|
418
|
+
},
|
|
419
|
+
];
|
|
420
|
+
const ts = await this.sendBlocks(channelId, blocks, question);
|
|
421
|
+
this.registerPendingPrompt(ts, promptId, channelId);
|
|
422
|
+
}
|
|
423
|
+
// ── Message Search ──
|
|
424
|
+
/** Search the JSONL message log. */
|
|
425
|
+
searchLog(params) {
|
|
426
|
+
return this.logger.search(params);
|
|
427
|
+
}
|
|
428
|
+
/** Get message log statistics. */
|
|
429
|
+
getLogStats() {
|
|
430
|
+
return this.logger.getStats();
|
|
431
|
+
}
|
|
432
|
+
// ── Channel Housekeeping ──
|
|
433
|
+
/**
|
|
434
|
+
* Auto-archive channels idle for more than AUTO_ARCHIVE_DAYS.
|
|
435
|
+
* Runs periodically. Only archives session channels (sess- prefix).
|
|
436
|
+
*/
|
|
437
|
+
async _archiveIdleChannels() {
|
|
438
|
+
try {
|
|
439
|
+
const channels = await this.channelManager.listChannels();
|
|
440
|
+
const now = Date.now();
|
|
441
|
+
const threshold = AUTO_ARCHIVE_DAYS * 24 * 60 * 60 * 1000;
|
|
442
|
+
for (const channel of channels) {
|
|
443
|
+
// Only auto-archive session channels, not system/job channels
|
|
444
|
+
if (!channel.name.includes('-sess-') || channel.is_archived)
|
|
445
|
+
continue;
|
|
446
|
+
// Check last message time from ring buffer
|
|
447
|
+
const history = this.channelHistory.get(channel.id);
|
|
448
|
+
const lastMessage = history?.toArray().at(-1);
|
|
449
|
+
if (lastMessage) {
|
|
450
|
+
const lastTs = parseFloat(lastMessage.ts) * 1000;
|
|
451
|
+
if (now - lastTs > threshold) {
|
|
452
|
+
await this.channelManager.archiveChannel(channel.id);
|
|
453
|
+
console.log(`[slack] Auto-archived idle channel: ${channel.name}`);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
catch (err) {
|
|
459
|
+
console.error('[slack] Channel housekeeping error:', err.message);
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
_startHousekeeping() {
|
|
463
|
+
// Run every 6 hours
|
|
464
|
+
this.housekeepingTimer = setInterval(() => {
|
|
465
|
+
this._archiveIdleChannels().catch(() => { });
|
|
466
|
+
}, 6 * 60 * 60 * 1000);
|
|
467
|
+
if (this.housekeepingTimer.unref)
|
|
468
|
+
this.housekeepingTimer.unref();
|
|
469
|
+
}
|
|
470
|
+
// ── Log Retention ──
|
|
471
|
+
/** Purge log entries older than logRetentionDays. */
|
|
472
|
+
_purgeOldLogs() {
|
|
473
|
+
const retentionDays = this.config.logRetentionDays ?? 90;
|
|
474
|
+
if (retentionDays === 0)
|
|
475
|
+
return; // Unlimited
|
|
476
|
+
const logPath = path.join(this.stateDir, 'slack-messages.jsonl');
|
|
477
|
+
if (!fs.existsSync(logPath))
|
|
478
|
+
return;
|
|
479
|
+
try {
|
|
480
|
+
const cutoff = new Date(Date.now() - retentionDays * 24 * 60 * 60 * 1000);
|
|
481
|
+
const content = fs.readFileSync(logPath, 'utf-8');
|
|
482
|
+
const lines = content.split('\n').filter(Boolean);
|
|
483
|
+
const kept = lines.filter(line => {
|
|
484
|
+
try {
|
|
485
|
+
const entry = JSON.parse(line);
|
|
486
|
+
return new Date(entry.timestamp) >= cutoff;
|
|
487
|
+
}
|
|
488
|
+
catch {
|
|
489
|
+
return true; // Keep unparseable lines
|
|
490
|
+
}
|
|
491
|
+
});
|
|
492
|
+
if (kept.length < lines.length) {
|
|
493
|
+
fs.writeFileSync(logPath, kept.join('\n') + '\n');
|
|
494
|
+
console.log(`[slack] Log purge: removed ${lines.length - kept.length} entries older than ${retentionDays} days`);
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
catch {
|
|
498
|
+
// Non-fatal — will retry on next cycle
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
_startLogPurge() {
|
|
502
|
+
this.logPurgeTimer = setInterval(() => {
|
|
503
|
+
this._purgeOldLogs();
|
|
504
|
+
}, LOG_PURGE_INTERVAL_MS);
|
|
505
|
+
if (this.logPurgeTimer.unref)
|
|
506
|
+
this.logPurgeTimer.unref();
|
|
507
|
+
}
|
|
508
|
+
// ── Dashboard ──
|
|
509
|
+
/**
|
|
510
|
+
* Broadcast the tunnel URL to the dashboard channel.
|
|
511
|
+
* Called by server.ts when tunnel is established.
|
|
512
|
+
*/
|
|
513
|
+
async broadcastDashboardUrl(tunnelUrl) {
|
|
514
|
+
const dashboardChannelId = this.config.dashboardChannelId;
|
|
515
|
+
if (!dashboardChannelId)
|
|
516
|
+
return;
|
|
517
|
+
const text = `Dashboard available at: ${tunnelUrl}`;
|
|
518
|
+
try {
|
|
519
|
+
await this.sendToChannel(dashboardChannelId, text);
|
|
520
|
+
await this.pinMessage(dashboardChannelId, (await this.sendToChannel(dashboardChannelId, text)));
|
|
521
|
+
}
|
|
522
|
+
catch (err) {
|
|
523
|
+
console.error('[slack] Dashboard broadcast failed:', err.message);
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
// ── Unanswered Message Detection ──
|
|
527
|
+
/**
|
|
528
|
+
* Get count of unanswered user messages in a channel.
|
|
529
|
+
* A message is "unanswered" if it's from a user and no agent reply follows.
|
|
530
|
+
*/
|
|
531
|
+
getUnansweredCount(channelId) {
|
|
532
|
+
const messages = this.getChannelMessages(channelId);
|
|
533
|
+
let unanswered = 0;
|
|
534
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
535
|
+
const msg = messages[i];
|
|
536
|
+
// Messages from authorized users are "user" messages
|
|
537
|
+
if (this.authorizedUsers.has(msg.user)) {
|
|
538
|
+
unanswered++;
|
|
539
|
+
}
|
|
540
|
+
else {
|
|
541
|
+
break; // Agent reply found — stop counting
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
return unanswered;
|
|
545
|
+
}
|
|
546
|
+
_chunkText(text) {
|
|
547
|
+
if (text.length <= SLACK_MAX_TEXT_LENGTH)
|
|
548
|
+
return [text];
|
|
549
|
+
const chunks = [];
|
|
550
|
+
let remaining = text;
|
|
551
|
+
while (remaining.length > 0) {
|
|
552
|
+
if (remaining.length <= SLACK_MAX_TEXT_LENGTH) {
|
|
553
|
+
chunks.push(remaining);
|
|
554
|
+
break;
|
|
555
|
+
}
|
|
556
|
+
// Try to break at a newline
|
|
557
|
+
let breakPoint = remaining.lastIndexOf('\n', SLACK_MAX_TEXT_LENGTH);
|
|
558
|
+
if (breakPoint < SLACK_MAX_TEXT_LENGTH / 2) {
|
|
559
|
+
// No good newline break — try space
|
|
560
|
+
breakPoint = remaining.lastIndexOf(' ', SLACK_MAX_TEXT_LENGTH);
|
|
561
|
+
}
|
|
562
|
+
if (breakPoint < SLACK_MAX_TEXT_LENGTH / 2) {
|
|
563
|
+
// No good break point — hard break
|
|
564
|
+
breakPoint = SLACK_MAX_TEXT_LENGTH;
|
|
565
|
+
}
|
|
566
|
+
chunks.push(remaining.slice(0, breakPoint));
|
|
567
|
+
remaining = remaining.slice(breakPoint).trimStart();
|
|
568
|
+
}
|
|
569
|
+
return chunks;
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
//# sourceMappingURL=SlackAdapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SlackAdapter.js","sourceRoot":"","sources":["../../../src/messaging/slack/SlackAdapter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SlackApiClient — Zero-SDK HTTP client for the Slack Web API.
|
|
3
|
+
*
|
|
4
|
+
* All Slack API calls go through this class, which handles:
|
|
5
|
+
* - Authentication (bot token vs app token)
|
|
6
|
+
* - Rate limit detection and retry (Retry-After header)
|
|
7
|
+
* - Error classification (permanent vs transient)
|
|
8
|
+
* - Token redaction in logs
|
|
9
|
+
*/
|
|
10
|
+
import { type RateLimitTier } from './types.js';
|
|
11
|
+
export interface SlackApiOptions {
|
|
12
|
+
/** Use app-level token instead of bot token */
|
|
13
|
+
useAppToken?: boolean;
|
|
14
|
+
/** Max retries on rate limit (default: 3) */
|
|
15
|
+
maxRetries?: number;
|
|
16
|
+
}
|
|
17
|
+
export interface SlackApiResponse {
|
|
18
|
+
ok: boolean;
|
|
19
|
+
error?: string;
|
|
20
|
+
response_metadata?: {
|
|
21
|
+
next_cursor?: string;
|
|
22
|
+
retry_after?: number;
|
|
23
|
+
};
|
|
24
|
+
[key: string]: unknown;
|
|
25
|
+
}
|
|
26
|
+
export declare class SlackApiClient {
|
|
27
|
+
private botToken;
|
|
28
|
+
private appToken;
|
|
29
|
+
constructor(botToken: string, appToken?: string);
|
|
30
|
+
/**
|
|
31
|
+
* Call a Slack Web API method.
|
|
32
|
+
*
|
|
33
|
+
* @param method - API method name (e.g., 'chat.postMessage')
|
|
34
|
+
* @param params - JSON body parameters
|
|
35
|
+
* @param options - Token selection and retry options
|
|
36
|
+
* @returns Parsed JSON response
|
|
37
|
+
* @throws Error on non-ok response (after retries for rate limits)
|
|
38
|
+
*/
|
|
39
|
+
call(method: string, params?: Record<string, unknown>, options?: SlackApiOptions): Promise<SlackApiResponse>;
|
|
40
|
+
/** Get the rate limit tier for a method. */
|
|
41
|
+
getTier(method: string): RateLimitTier;
|
|
42
|
+
private _callWithRetry;
|
|
43
|
+
}
|
|
44
|
+
/** Typed error for Slack API failures. */
|
|
45
|
+
export declare class SlackApiError extends Error {
|
|
46
|
+
readonly method: string;
|
|
47
|
+
readonly slackError: string;
|
|
48
|
+
readonly permanent: boolean;
|
|
49
|
+
constructor(message: string, method: string, slackError: string, permanent: boolean);
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=SlackApiClient.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SlackApiClient.d.ts","sourceRoot":"","sources":["../../../src/messaging/slack/SlackApiClient.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAW,KAAK,aAAa,EAAE,MAAM,YAAY,CAAC;AAGzD,MAAM,WAAW,eAAe;IAC9B,+CAA+C;IAC/C,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,6CAA6C;IAC7C,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,OAAO,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,iBAAiB,CAAC,EAAE;QAClB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,WAAW,CAAC,EAAE,MAAM,CAAC;KACtB,CAAC;IACF,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAcD,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,QAAQ,CAAgB;gBAEpB,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM;IAK/C;;;;;;;;OAQG;IACG,IAAI,CACR,MAAM,EAAE,MAAM,EACd,MAAM,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,EACpC,OAAO,GAAE,eAAoB,GAC5B,OAAO,CAAC,gBAAgB,CAAC;IAU5B,4CAA4C;IAC5C,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,aAAa;YAIxB,cAAc;CA8C7B;AAED,0CAA0C;AAC1C,qBAAa,aAAc,SAAQ,KAAK;IACtC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;gBAEhB,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO;CAOpF"}
|