@pinet/slack-bridge 0.1.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/LICENSE +21 -0
- package/README.md +570 -0
- package/dist/activity-log.d.ts +62 -0
- package/dist/activity-log.js +293 -0
- package/dist/agent-completion-runtime.d.ts +17 -0
- package/dist/agent-completion-runtime.js +26 -0
- package/dist/agent-event-runtime.d.ts +13 -0
- package/dist/agent-event-runtime.js +27 -0
- package/dist/agent-prompt-guidance.d.ts +22 -0
- package/dist/agent-prompt-guidance.js +46 -0
- package/dist/broker/adapters/slack.d.ts +74 -0
- package/dist/broker/adapters/slack.js +563 -0
- package/dist/broker/adapters/types.d.ts +1 -0
- package/dist/broker/adapters/types.js +1 -0
- package/dist/broker/agent-messaging.d.ts +1 -0
- package/dist/broker/agent-messaging.js +1 -0
- package/dist/broker/auth.d.ts +1 -0
- package/dist/broker/auth.js +1 -0
- package/dist/broker/client.d.ts +167 -0
- package/dist/broker/client.js +624 -0
- package/dist/broker/control-plane-dashboard.d.ts +98 -0
- package/dist/broker/control-plane-dashboard.js +213 -0
- package/dist/broker/ghost-reaper.d.ts +49 -0
- package/dist/broker/ghost-reaper.js +209 -0
- package/dist/broker/index.d.ts +36 -0
- package/dist/broker/index.js +90 -0
- package/dist/broker/leader.d.ts +1 -0
- package/dist/broker/leader.js +1 -0
- package/dist/broker/maintenance.d.ts +1 -0
- package/dist/broker/maintenance.js +1 -0
- package/dist/broker/message-send.d.ts +1 -0
- package/dist/broker/message-send.js +1 -0
- package/dist/broker/paths.d.ts +1 -0
- package/dist/broker/paths.js +1 -0
- package/dist/broker/raw-tcp-loopback.d.ts +1 -0
- package/dist/broker/raw-tcp-loopback.js +1 -0
- package/dist/broker/router.d.ts +1 -0
- package/dist/broker/router.js +1 -0
- package/dist/broker/schema.d.ts +9 -0
- package/dist/broker/schema.js +63 -0
- package/dist/broker/socket-server.d.ts +120 -0
- package/dist/broker/socket-server.js +1087 -0
- package/dist/broker/types.d.ts +1 -0
- package/dist/broker/types.js +1 -0
- package/dist/broker-delivery.d.ts +10 -0
- package/dist/broker-delivery.js +26 -0
- package/dist/broker-inbound-persistence.d.ts +11 -0
- package/dist/broker-inbound-persistence.js +22 -0
- package/dist/broker-prompt-loader.d.ts +34 -0
- package/dist/broker-prompt-loader.js +185 -0
- package/dist/broker-runtime-access.d.ts +14 -0
- package/dist/broker-runtime-access.js +20 -0
- package/dist/broker-runtime.d.ts +100 -0
- package/dist/broker-runtime.js +533 -0
- package/dist/broker-thread-owner-hints.d.ts +10 -0
- package/dist/broker-thread-owner-hints.js +14 -0
- package/dist/canvases.d.ts +80 -0
- package/dist/canvases.js +221 -0
- package/dist/command-registration-runtime.d.ts +9 -0
- package/dist/command-registration-runtime.js +9 -0
- package/dist/core-tool-guardrails.d.ts +24 -0
- package/dist/core-tool-guardrails.js +66 -0
- package/dist/deploy-manifest.d.ts +27 -0
- package/dist/deploy-manifest.js +197 -0
- package/dist/follower-delivery.d.ts +16 -0
- package/dist/follower-delivery.js +70 -0
- package/dist/follower-runtime.d.ts +61 -0
- package/dist/follower-runtime.js +350 -0
- package/dist/git-metadata.d.ts +35 -0
- package/dist/git-metadata.js +88 -0
- package/dist/guardrails.d.ts +69 -0
- package/dist/guardrails.js +283 -0
- package/dist/helpers.d.ts +550 -0
- package/dist/helpers.js +2711 -0
- package/dist/home-tab.d.ts +27 -0
- package/dist/home-tab.js +242 -0
- package/dist/imessage-tools.d.ts +33 -0
- package/dist/imessage-tools.js +96 -0
- package/dist/inbox-drain-runtime.d.ts +30 -0
- package/dist/inbox-drain-runtime.js +71 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1330 -0
- package/dist/persisted-runtime-state.d.ts +52 -0
- package/dist/persisted-runtime-state.js +98 -0
- package/dist/pinet-activity-formatting.d.ts +20 -0
- package/dist/pinet-activity-formatting.js +43 -0
- package/dist/pinet-agent-status.d.ts +36 -0
- package/dist/pinet-agent-status.js +48 -0
- package/dist/pinet-commands.d.ts +70 -0
- package/dist/pinet-commands.js +402 -0
- package/dist/pinet-confirmation-replies.d.ts +7 -0
- package/dist/pinet-confirmation-replies.js +30 -0
- package/dist/pinet-control-plane-dashboard.d.ts +34 -0
- package/dist/pinet-control-plane-dashboard.js +86 -0
- package/dist/pinet-home-tabs.d.ts +36 -0
- package/dist/pinet-home-tabs.js +101 -0
- package/dist/pinet-maintenance-delivery.d.ts +22 -0
- package/dist/pinet-maintenance-delivery.js +45 -0
- package/dist/pinet-mesh-ops.d.ts +97 -0
- package/dist/pinet-mesh-ops.js +266 -0
- package/dist/pinet-registration-gate.d.ts +14 -0
- package/dist/pinet-registration-gate.js +42 -0
- package/dist/pinet-remote-control-acks.d.ts +16 -0
- package/dist/pinet-remote-control-acks.js +46 -0
- package/dist/pinet-remote-control.d.ts +17 -0
- package/dist/pinet-remote-control.js +74 -0
- package/dist/pinet-runtime-composition.d.ts +27 -0
- package/dist/pinet-runtime-composition.js +20 -0
- package/dist/pinet-tools.d.ts +67 -0
- package/dist/pinet-tools.js +1211 -0
- package/dist/prompts/broker/default.md +41 -0
- package/dist/prompts/broker/tmux.md +49 -0
- package/dist/ralph-loop.d.ts +76 -0
- package/dist/ralph-loop.js +524 -0
- package/dist/reaction-triggers.d.ts +25 -0
- package/dist/reaction-triggers.js +171 -0
- package/dist/repo-tool-guardrails.d.ts +20 -0
- package/dist/repo-tool-guardrails.js +81 -0
- package/dist/runtime-agent-context.d.ts +93 -0
- package/dist/runtime-agent-context.js +302 -0
- package/dist/runtime-mode.d.ts +10 -0
- package/dist/runtime-mode.js +39 -0
- package/dist/scheduled-wakeups.d.ts +1 -0
- package/dist/scheduled-wakeups.js +1 -0
- package/dist/session-ui-runtime.d.ts +20 -0
- package/dist/session-ui-runtime.js +163 -0
- package/dist/single-player-runtime.d.ts +71 -0
- package/dist/single-player-runtime.js +436 -0
- package/dist/skins/cosmere.json +1571 -0
- package/dist/skins/foundation.json +1304 -0
- package/dist/slack-access.d.ts +186 -0
- package/dist/slack-access.js +462 -0
- package/dist/slack-api.d.ts +2 -0
- package/dist/slack-api.js +1 -0
- package/dist/slack-block-kit.d.ts +36 -0
- package/dist/slack-block-kit.js +242 -0
- package/dist/slack-export.d.ts +31 -0
- package/dist/slack-export.js +206 -0
- package/dist/slack-message-context.d.ts +15 -0
- package/dist/slack-message-context.js +207 -0
- package/dist/slack-modals.d.ts +13 -0
- package/dist/slack-modals.js +79 -0
- package/dist/slack-pinet-runtime-adapter.d.ts +16 -0
- package/dist/slack-pinet-runtime-adapter.js +72 -0
- package/dist/slack-presence.d.ts +36 -0
- package/dist/slack-presence.js +89 -0
- package/dist/slack-request-runtime.d.ts +8 -0
- package/dist/slack-request-runtime.js +19 -0
- package/dist/slack-runtime-access.d.ts +28 -0
- package/dist/slack-runtime-access.js +107 -0
- package/dist/slack-scope-diagnostics.d.ts +31 -0
- package/dist/slack-scope-diagnostics.js +235 -0
- package/dist/slack-socket-dedup.d.ts +7 -0
- package/dist/slack-socket-dedup.js +135 -0
- package/dist/slack-thread-status.d.ts +41 -0
- package/dist/slack-thread-status.js +156 -0
- package/dist/slack-tool-policy-runtime.d.ts +36 -0
- package/dist/slack-tool-policy-runtime.js +96 -0
- package/dist/slack-tools.d.ts +52 -0
- package/dist/slack-tools.js +2688 -0
- package/dist/slack-turn-guardrails.d.ts +19 -0
- package/dist/slack-turn-guardrails.js +36 -0
- package/dist/slack-upload.d.ts +49 -0
- package/dist/slack-upload.js +224 -0
- package/dist/task-assignments.d.ts +45 -0
- package/dist/task-assignments.js +527 -0
- package/dist/thread-confirmations.d.ts +22 -0
- package/dist/thread-confirmations.js +139 -0
- package/dist/tool-registration-runtime.d.ts +13 -0
- package/dist/tool-registration-runtime.js +13 -0
- package/dist/ttl-cache.d.ts +47 -0
- package/dist/ttl-cache.js +105 -0
- package/manifest.yaml +57 -0
- package/package.json +60 -0
- package/skills/pinet-skin-creator/SKILL.md +109 -0
- package/skills/pinet-skin-creator/references/descriptor-format.md +99 -0
- package/skills/pinet-skin-creator/references/safety-checklist.md +56 -0
- package/skills/pinet-skin-creator/templates/pinet-skin-descriptor.json +77 -0
- package/skills/slack-bridge/SKILL.md +360 -0
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
const LEVEL_RANK = {
|
|
2
|
+
errors: 0,
|
|
3
|
+
actions: 1,
|
|
4
|
+
verbose: 2,
|
|
5
|
+
};
|
|
6
|
+
const DEFAULT_MAX_RECENT_ENTRIES = 100;
|
|
7
|
+
const MAX_TEXT_LENGTH = 2800;
|
|
8
|
+
const REDACTED = "[REDACTED]";
|
|
9
|
+
function normalizeWhitespace(value) {
|
|
10
|
+
return value.replace(/\r\n/g, "\n").replaceAll("\u0000", "").trim();
|
|
11
|
+
}
|
|
12
|
+
function truncate(value, maxLength = MAX_TEXT_LENGTH) {
|
|
13
|
+
if (value.length <= maxLength) {
|
|
14
|
+
return value;
|
|
15
|
+
}
|
|
16
|
+
return `${value.slice(0, Math.max(0, maxLength - 3)).trimEnd()}...`;
|
|
17
|
+
}
|
|
18
|
+
export function normalizeActivityLogLevel(value) {
|
|
19
|
+
const normalized = value?.trim().toLowerCase();
|
|
20
|
+
if (normalized === "errors" || normalized === "actions" || normalized === "verbose") {
|
|
21
|
+
return normalized;
|
|
22
|
+
}
|
|
23
|
+
return "actions";
|
|
24
|
+
}
|
|
25
|
+
export function shouldLogActivity(configuredLevel, eventLevel) {
|
|
26
|
+
return LEVEL_RANK[eventLevel] <= LEVEL_RANK[configuredLevel];
|
|
27
|
+
}
|
|
28
|
+
export function redactSensitiveText(value) {
|
|
29
|
+
let redacted = normalizeWhitespace(value);
|
|
30
|
+
const replacements = [
|
|
31
|
+
[/xox[baprs]-[A-Za-z0-9-]+/g, REDACTED],
|
|
32
|
+
[/xoxe\.[A-Za-z0-9.-]+/g, REDACTED],
|
|
33
|
+
[/(Bearer\s+)[^\s]+/gi, `$1${REDACTED}`],
|
|
34
|
+
[
|
|
35
|
+
/\b(token|password|secret|api[_-]?key|authorization)\b\s*([:=])\s*([^\s,;]+)/gi,
|
|
36
|
+
`$1$2 ${REDACTED}`,
|
|
37
|
+
],
|
|
38
|
+
[
|
|
39
|
+
/("(?:token|password|secret|api[_-]?key|authorization)"\s*:\s*")([^"]+)(")/gi,
|
|
40
|
+
`$1${REDACTED}$3`,
|
|
41
|
+
],
|
|
42
|
+
[/(SLACK_[A-Z_]+)=([^\s]+)/g, `$1=${REDACTED}`],
|
|
43
|
+
];
|
|
44
|
+
for (const [pattern, replacement] of replacements) {
|
|
45
|
+
redacted = redacted.replace(pattern, replacement);
|
|
46
|
+
}
|
|
47
|
+
return truncate(redacted);
|
|
48
|
+
}
|
|
49
|
+
function sanitizeFieldValue(value) {
|
|
50
|
+
if (value == null) {
|
|
51
|
+
return "—";
|
|
52
|
+
}
|
|
53
|
+
if (typeof value === "boolean") {
|
|
54
|
+
return value ? "yes" : "no";
|
|
55
|
+
}
|
|
56
|
+
return redactSensitiveText(String(value));
|
|
57
|
+
}
|
|
58
|
+
function sanitizeEntry(entry, now) {
|
|
59
|
+
return {
|
|
60
|
+
...entry,
|
|
61
|
+
title: redactSensitiveText(entry.title),
|
|
62
|
+
summary: redactSensitiveText(entry.summary),
|
|
63
|
+
details: entry.details?.map((detail) => redactSensitiveText(detail)).filter(Boolean),
|
|
64
|
+
fields: entry.fields
|
|
65
|
+
?.map((field) => ({
|
|
66
|
+
label: redactSensitiveText(field.label),
|
|
67
|
+
value: sanitizeFieldValue(field.value),
|
|
68
|
+
}))
|
|
69
|
+
.filter((field) => field.label.length > 0),
|
|
70
|
+
timestamp: entry.timestamp ?? now.toISOString(),
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
function getToneEmoji(tone) {
|
|
74
|
+
switch (tone) {
|
|
75
|
+
case "success":
|
|
76
|
+
return "✅";
|
|
77
|
+
case "warning":
|
|
78
|
+
return "⚠️";
|
|
79
|
+
case "error":
|
|
80
|
+
return "🚨";
|
|
81
|
+
case "info":
|
|
82
|
+
default:
|
|
83
|
+
return "📡";
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
function formatSlackTimestamp(timestamp) {
|
|
87
|
+
const date = new Date(timestamp);
|
|
88
|
+
if (Number.isNaN(date.getTime())) {
|
|
89
|
+
return timestamp;
|
|
90
|
+
}
|
|
91
|
+
return date.toISOString().replace(".000Z", "Z");
|
|
92
|
+
}
|
|
93
|
+
export function buildActivityLogText(agentName, agentEmoji, entry) {
|
|
94
|
+
return `${getToneEmoji(entry.tone)} ${entry.title} — ${entry.summary} (${agentEmoji} ${agentName} · ${formatSlackTimestamp(entry.timestamp)})`;
|
|
95
|
+
}
|
|
96
|
+
export function buildActivityLogBlocks(agentName, agentEmoji, entry) {
|
|
97
|
+
const blocks = [
|
|
98
|
+
{
|
|
99
|
+
type: "section",
|
|
100
|
+
text: {
|
|
101
|
+
type: "mrkdwn",
|
|
102
|
+
text: `*${getToneEmoji(entry.tone)} ${entry.title}*\n${entry.summary}`,
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
];
|
|
106
|
+
const fields = (entry.fields ?? [])
|
|
107
|
+
.filter((field) => field.value !== "—")
|
|
108
|
+
.slice(0, 10)
|
|
109
|
+
.map((field) => ({
|
|
110
|
+
type: "mrkdwn",
|
|
111
|
+
text: `*${field.label}*\n${field.value}`,
|
|
112
|
+
}));
|
|
113
|
+
if (fields.length > 0) {
|
|
114
|
+
blocks.push({ type: "section", fields });
|
|
115
|
+
}
|
|
116
|
+
if (entry.details && entry.details.length > 0) {
|
|
117
|
+
blocks.push({
|
|
118
|
+
type: "section",
|
|
119
|
+
text: {
|
|
120
|
+
type: "mrkdwn",
|
|
121
|
+
text: entry.details.map((detail) => `• ${detail}`).join("\n"),
|
|
122
|
+
},
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
blocks.push({
|
|
126
|
+
type: "context",
|
|
127
|
+
elements: [
|
|
128
|
+
{
|
|
129
|
+
type: "mrkdwn",
|
|
130
|
+
text: `${agentEmoji} ${agentName} · ${entry.kind} · ${formatSlackTimestamp(entry.timestamp)}`,
|
|
131
|
+
},
|
|
132
|
+
],
|
|
133
|
+
});
|
|
134
|
+
return blocks;
|
|
135
|
+
}
|
|
136
|
+
export function buildActivityLogThreadHeader(agentName, agentEmoji, dateKey) {
|
|
137
|
+
return {
|
|
138
|
+
text: `Pinet activity log — ${dateKey}`,
|
|
139
|
+
blocks: [
|
|
140
|
+
{
|
|
141
|
+
type: "section",
|
|
142
|
+
text: {
|
|
143
|
+
type: "mrkdwn",
|
|
144
|
+
text: `*📚 Pinet activity log — ${dateKey}*\nBroker-side coordination updates: assignments, completions, merges, stalls, and RALPH maintenance events.`,
|
|
145
|
+
},
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
type: "context",
|
|
149
|
+
elements: [
|
|
150
|
+
{
|
|
151
|
+
type: "mrkdwn",
|
|
152
|
+
text: `${agentEmoji} ${agentName} · daily thread`,
|
|
153
|
+
},
|
|
154
|
+
],
|
|
155
|
+
},
|
|
156
|
+
],
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
export function formatRecentActivityLogEntries(entries) {
|
|
160
|
+
if (entries.length === 0) {
|
|
161
|
+
return "No activity log entries recorded in this session.";
|
|
162
|
+
}
|
|
163
|
+
return entries
|
|
164
|
+
.map((entry) => `[${formatSlackTimestamp(entry.timestamp)}] ${entry.title} — ${entry.summary}`)
|
|
165
|
+
.join("\n");
|
|
166
|
+
}
|
|
167
|
+
export class SlackActivityLogger {
|
|
168
|
+
deps;
|
|
169
|
+
queue = [];
|
|
170
|
+
recent = [];
|
|
171
|
+
maxRecentEntries;
|
|
172
|
+
flushTimer = null;
|
|
173
|
+
flushRunning = false;
|
|
174
|
+
resolvedChannelCache = null;
|
|
175
|
+
dailyThreadCache = null;
|
|
176
|
+
constructor(deps) {
|
|
177
|
+
this.deps = deps;
|
|
178
|
+
this.maxRecentEntries = deps.maxRecentEntries ?? DEFAULT_MAX_RECENT_ENTRIES;
|
|
179
|
+
}
|
|
180
|
+
log(entry) {
|
|
181
|
+
const rawChannel = this.deps.getLogChannel()?.trim();
|
|
182
|
+
if (!rawChannel) {
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
const configuredLevel = normalizeActivityLogLevel(this.deps.getLogLevel());
|
|
186
|
+
if (!shouldLogActivity(configuredLevel, entry.level)) {
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
const sanitized = sanitizeEntry(entry, this.getNow());
|
|
190
|
+
this.recent.unshift(sanitized);
|
|
191
|
+
this.recent.splice(this.maxRecentEntries);
|
|
192
|
+
this.queue.push({ entry: sanitized, attempts: 0 });
|
|
193
|
+
this.scheduleFlush(0);
|
|
194
|
+
}
|
|
195
|
+
getRecentEntries(limit = 20) {
|
|
196
|
+
return this.recent.slice(0, Math.max(0, limit));
|
|
197
|
+
}
|
|
198
|
+
clearPending() {
|
|
199
|
+
this.queue.splice(0, this.queue.length);
|
|
200
|
+
this.resolvedChannelCache = null;
|
|
201
|
+
this.dailyThreadCache = null;
|
|
202
|
+
if (this.flushTimer) {
|
|
203
|
+
clearTimeout(this.flushTimer);
|
|
204
|
+
this.flushTimer = null;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
getNow() {
|
|
208
|
+
return this.deps.now ? this.deps.now() : new Date();
|
|
209
|
+
}
|
|
210
|
+
scheduleFlush(delayMs) {
|
|
211
|
+
if (this.flushTimer || this.flushRunning || this.queue.length === 0) {
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
this.flushTimer = setTimeout(() => {
|
|
215
|
+
this.flushTimer = null;
|
|
216
|
+
void this.flushNext();
|
|
217
|
+
}, delayMs);
|
|
218
|
+
this.flushTimer.unref?.();
|
|
219
|
+
}
|
|
220
|
+
async flushNext() {
|
|
221
|
+
if (this.flushRunning) {
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
const next = this.queue.shift();
|
|
225
|
+
if (!next) {
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
this.flushRunning = true;
|
|
229
|
+
try {
|
|
230
|
+
await this.postEntry(next.entry);
|
|
231
|
+
}
|
|
232
|
+
catch (error) {
|
|
233
|
+
if (next.attempts < 2) {
|
|
234
|
+
this.queue.unshift({ entry: next.entry, attempts: next.attempts + 1 });
|
|
235
|
+
}
|
|
236
|
+
else {
|
|
237
|
+
this.deps.onError?.(error);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
finally {
|
|
241
|
+
this.flushRunning = false;
|
|
242
|
+
if (this.queue.length > 0) {
|
|
243
|
+
this.scheduleFlush(1000);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
async resolveLogChannel(rawChannel) {
|
|
248
|
+
if (this.resolvedChannelCache?.raw === rawChannel) {
|
|
249
|
+
return this.resolvedChannelCache.id;
|
|
250
|
+
}
|
|
251
|
+
const channelId = await this.deps.resolveChannel(rawChannel);
|
|
252
|
+
this.resolvedChannelCache = { raw: rawChannel, id: channelId };
|
|
253
|
+
return channelId;
|
|
254
|
+
}
|
|
255
|
+
async ensureDailyThread(rawChannel, channelId) {
|
|
256
|
+
const dateKey = this.getNow().toISOString().slice(0, 10);
|
|
257
|
+
if (this.dailyThreadCache?.rawChannel === rawChannel &&
|
|
258
|
+
this.dailyThreadCache.dateKey === dateKey) {
|
|
259
|
+
return this.dailyThreadCache.threadTs;
|
|
260
|
+
}
|
|
261
|
+
const token = this.deps.getBotToken();
|
|
262
|
+
if (!token) {
|
|
263
|
+
throw new Error("Slack bot token unavailable for activity logging.");
|
|
264
|
+
}
|
|
265
|
+
const heading = buildActivityLogThreadHeader(this.deps.getAgentName(), this.deps.getAgentEmoji(), dateKey);
|
|
266
|
+
const response = await this.deps.slack("chat.postMessage", token, {
|
|
267
|
+
channel: channelId,
|
|
268
|
+
text: heading.text,
|
|
269
|
+
blocks: heading.blocks,
|
|
270
|
+
});
|
|
271
|
+
const threadTs = typeof response.ts === "string" ? response.ts : null;
|
|
272
|
+
if (!threadTs) {
|
|
273
|
+
throw new Error("Slack activity log thread creation did not return a ts.");
|
|
274
|
+
}
|
|
275
|
+
this.dailyThreadCache = { rawChannel, dateKey, threadTs };
|
|
276
|
+
return threadTs;
|
|
277
|
+
}
|
|
278
|
+
async postEntry(entry) {
|
|
279
|
+
const rawChannel = this.deps.getLogChannel()?.trim();
|
|
280
|
+
const token = this.deps.getBotToken();
|
|
281
|
+
if (!rawChannel || !token) {
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
const channelId = await this.resolveLogChannel(rawChannel);
|
|
285
|
+
const threadTs = await this.ensureDailyThread(rawChannel, channelId);
|
|
286
|
+
await this.deps.slack("chat.postMessage", token, {
|
|
287
|
+
channel: channelId,
|
|
288
|
+
thread_ts: threadTs,
|
|
289
|
+
text: buildActivityLogText(this.deps.getAgentName(), this.deps.getAgentEmoji(), entry),
|
|
290
|
+
blocks: buildActivityLogBlocks(this.deps.getAgentName(), this.deps.getAgentEmoji(), entry),
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
interface AgentCompletionThreadState {
|
|
3
|
+
channelId: string;
|
|
4
|
+
}
|
|
5
|
+
export interface AgentCompletionRuntimeDeps {
|
|
6
|
+
getThreads: () => Map<string, AgentCompletionThreadState>;
|
|
7
|
+
clearThreadStatus: (channelId: string, threadTs: string) => Promise<void>;
|
|
8
|
+
clearFollowUpPending: () => void;
|
|
9
|
+
signalAgentFree: (ctx: ExtensionContext) => Promise<unknown>;
|
|
10
|
+
formatError: (error: unknown) => string;
|
|
11
|
+
}
|
|
12
|
+
export interface AgentCompletionRuntime {
|
|
13
|
+
trackThinkingThread: (threadTs: string) => void;
|
|
14
|
+
onAgentEnd: (_event: unknown, ctx: ExtensionContext) => Promise<void>;
|
|
15
|
+
}
|
|
16
|
+
export declare function createAgentCompletionRuntime(deps: AgentCompletionRuntimeDeps): AgentCompletionRuntime;
|
|
17
|
+
export {};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export function createAgentCompletionRuntime(deps) {
|
|
2
|
+
const thinking = new Set();
|
|
3
|
+
function trackThinkingThread(threadTs) {
|
|
4
|
+
thinking.add(threadTs);
|
|
5
|
+
}
|
|
6
|
+
async function onAgentEnd(_event, ctx) {
|
|
7
|
+
for (const threadTs of thinking) {
|
|
8
|
+
const thread = deps.getThreads().get(threadTs);
|
|
9
|
+
if (thread) {
|
|
10
|
+
await deps.clearThreadStatus(thread.channelId, threadTs);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
thinking.clear();
|
|
14
|
+
deps.clearFollowUpPending();
|
|
15
|
+
try {
|
|
16
|
+
await deps.signalAgentFree(ctx);
|
|
17
|
+
}
|
|
18
|
+
catch (err) {
|
|
19
|
+
ctx.ui.notify(`Pinet auto-free failed: ${deps.formatError(err)}`, "warning");
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return {
|
|
23
|
+
trackThinkingThread,
|
|
24
|
+
onAgentEnd,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import type { AgentCompletionRuntime } from "./agent-completion-runtime.js";
|
|
3
|
+
import type { AgentPromptGuidance } from "./agent-prompt-guidance.js";
|
|
4
|
+
import { type SlackToolPolicyRuntime, type SlackToolPolicyRuntimeDeps } from "./slack-tool-policy-runtime.js";
|
|
5
|
+
export interface AgentEventRuntimeDeps extends SlackToolPolicyRuntimeDeps {
|
|
6
|
+
beforeAgentStart: AgentPromptGuidance["beforeAgentStart"];
|
|
7
|
+
onCompletionAgentEnd: AgentCompletionRuntime["onAgentEnd"];
|
|
8
|
+
setDeliverTrackedSlackFollowUpMessage: (deliver: SlackToolPolicyRuntime["deliverTrackedSlackFollowUpMessage"]) => void;
|
|
9
|
+
}
|
|
10
|
+
export interface AgentEventRuntime {
|
|
11
|
+
register: (pi: Pick<ExtensionAPI, "on">) => void;
|
|
12
|
+
}
|
|
13
|
+
export declare function createAgentEventRuntime(deps: AgentEventRuntimeDeps): AgentEventRuntime;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { createSlackToolPolicyRuntime, } from "./slack-tool-policy-runtime.js";
|
|
2
|
+
export function createAgentEventRuntime(deps) {
|
|
3
|
+
const slackToolPolicyRuntime = createSlackToolPolicyRuntime({
|
|
4
|
+
getBrokerRole: deps.getBrokerRole,
|
|
5
|
+
getGuardrails: deps.getGuardrails,
|
|
6
|
+
requireToolPolicy: deps.requireToolPolicy,
|
|
7
|
+
formatAction: deps.formatAction,
|
|
8
|
+
formatError: deps.formatError,
|
|
9
|
+
deliverFollowUpMessage: deps.deliverFollowUpMessage,
|
|
10
|
+
beginThreadStatus: deps.beginThreadStatus,
|
|
11
|
+
updateThreadStatus: deps.updateThreadStatus,
|
|
12
|
+
clearThreadStatus: deps.clearThreadStatus,
|
|
13
|
+
});
|
|
14
|
+
deps.setDeliverTrackedSlackFollowUpMessage(slackToolPolicyRuntime.deliverTrackedSlackFollowUpMessage);
|
|
15
|
+
function register(pi) {
|
|
16
|
+
pi.on("input", slackToolPolicyRuntime.onInput);
|
|
17
|
+
pi.on("turn_start", slackToolPolicyRuntime.onTurnStart);
|
|
18
|
+
pi.on("turn_end", slackToolPolicyRuntime.onTurnEnd);
|
|
19
|
+
pi.on("agent_end", slackToolPolicyRuntime.onAgentEnd);
|
|
20
|
+
pi.on("tool_call", slackToolPolicyRuntime.onToolCall);
|
|
21
|
+
pi.on("before_agent_start", deps.beforeAgentStart);
|
|
22
|
+
pi.on("agent_end", deps.onCompletionAgentEnd);
|
|
23
|
+
}
|
|
24
|
+
return {
|
|
25
|
+
register,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type BrokerPromptLoadResult } from "./broker-prompt-loader.js";
|
|
2
|
+
export interface BeforeAgentStartEvent {
|
|
3
|
+
systemPrompt: string;
|
|
4
|
+
}
|
|
5
|
+
export interface AgentPromptGuidanceDeps {
|
|
6
|
+
getIdentityGuidelines: () => string[];
|
|
7
|
+
getAgentName: () => string;
|
|
8
|
+
getAgentEmoji: () => string;
|
|
9
|
+
getActiveSkinTheme: () => string | null;
|
|
10
|
+
getAgentPersonality: () => string | null;
|
|
11
|
+
getBrokerRole: () => "broker" | "follower" | null;
|
|
12
|
+
getBrokerPromptSetting?: () => string | undefined;
|
|
13
|
+
loadBrokerPrompt?: () => Promise<BrokerPromptLoadResult>;
|
|
14
|
+
reportBrokerPromptWarning?: (warning: string) => void;
|
|
15
|
+
reportBrokerPromptDiagnostic?: (diagnostic: string) => void;
|
|
16
|
+
}
|
|
17
|
+
export interface AgentPromptGuidance {
|
|
18
|
+
beforeAgentStart: (event: BeforeAgentStartEvent) => Promise<{
|
|
19
|
+
systemPrompt: string;
|
|
20
|
+
}>;
|
|
21
|
+
}
|
|
22
|
+
export declare function createAgentPromptGuidance(deps: AgentPromptGuidanceDeps): AgentPromptGuidance;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { buildAgentPersonalityGuidelines, buildBrokerProtocolGuardrailsPrompt, buildPinetSkinPromptGuideline, buildWorkerPromptGuidelines, } from "./helpers.js";
|
|
2
|
+
import { buildBrokerToolGuardrailsPrompt } from "./guardrails.js";
|
|
3
|
+
import { loadBrokerPrompt, renderBrokerPromptContent, } from "./broker-prompt-loader.js";
|
|
4
|
+
import { buildReactionPromptGuidelines } from "./reaction-triggers.js";
|
|
5
|
+
export function createAgentPromptGuidance(deps) {
|
|
6
|
+
async function buildPromptGuidelines() {
|
|
7
|
+
const agentName = deps.getAgentName();
|
|
8
|
+
const guidelines = [
|
|
9
|
+
...deps.getIdentityGuidelines(),
|
|
10
|
+
...buildAgentPersonalityGuidelines(agentName),
|
|
11
|
+
...buildReactionPromptGuidelines(),
|
|
12
|
+
];
|
|
13
|
+
const skinGuideline = buildPinetSkinPromptGuideline(deps.getActiveSkinTheme(), deps.getAgentPersonality());
|
|
14
|
+
if (skinGuideline) {
|
|
15
|
+
guidelines.push(skinGuideline);
|
|
16
|
+
}
|
|
17
|
+
if (deps.getBrokerRole() === "broker") {
|
|
18
|
+
const brokerPrompt = await (deps.loadBrokerPrompt ??
|
|
19
|
+
(() => loadBrokerPrompt({
|
|
20
|
+
configuredPrompt: deps.getBrokerPromptSetting?.(),
|
|
21
|
+
})))();
|
|
22
|
+
for (const warning of brokerPrompt.warnings) {
|
|
23
|
+
(deps.reportBrokerPromptWarning ?? console.warn)(`[slack-bridge] ${warning.message}`);
|
|
24
|
+
}
|
|
25
|
+
(deps.reportBrokerPromptDiagnostic ?? console.info)(`[slack-bridge] ${brokerPrompt.diagnostic}`);
|
|
26
|
+
guidelines.push(renderBrokerPromptContent(brokerPrompt.content, {
|
|
27
|
+
agentEmoji: deps.getAgentEmoji(),
|
|
28
|
+
agentName,
|
|
29
|
+
}));
|
|
30
|
+
guidelines.push(buildBrokerProtocolGuardrailsPrompt());
|
|
31
|
+
guidelines.push(buildBrokerToolGuardrailsPrompt());
|
|
32
|
+
}
|
|
33
|
+
else if (deps.getBrokerRole() === "follower") {
|
|
34
|
+
guidelines.push(...buildWorkerPromptGuidelines());
|
|
35
|
+
}
|
|
36
|
+
return guidelines;
|
|
37
|
+
}
|
|
38
|
+
async function beforeAgentStart(event) {
|
|
39
|
+
return {
|
|
40
|
+
systemPrompt: event.systemPrompt + "\n\n" + (await buildPromptGuidelines()).join("\n"),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
44
|
+
beforeAgentStart,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { type ParsedAppHomeOpened, type ParsedThreadStarted } from "../../slack-access.js";
|
|
2
|
+
import { type ReactionCommandSettings } from "../../reaction-triggers.js";
|
|
3
|
+
import type { AdapterCapabilityRequest, AdapterCapabilityResult, InboundMessage, OutboundMessage, MessageAdapter } from "./types.js";
|
|
4
|
+
export { classifyMessage, extractAppHomeOpened, extractThreadStarted, parseMemberJoinedChannel, parseSocketFrame, RECONNECT_DELAY_MS, } from "../../slack-access.js";
|
|
5
|
+
export interface SlackAdapterConfig {
|
|
6
|
+
botToken: string;
|
|
7
|
+
appToken: string;
|
|
8
|
+
allowedUsers?: string[];
|
|
9
|
+
allowAllWorkspaceUsers?: boolean;
|
|
10
|
+
suggestedPrompts?: {
|
|
11
|
+
title: string;
|
|
12
|
+
message: string;
|
|
13
|
+
}[];
|
|
14
|
+
reactionCommands?: ReactionCommandSettings;
|
|
15
|
+
/** Check whether a thread_ts belongs to a known thread in the broker DB. */
|
|
16
|
+
isKnownThread?: (threadTs: string) => boolean;
|
|
17
|
+
/** Load durable thread metadata from the broker DB after cache eviction. */
|
|
18
|
+
getKnownThread?: (threadTs: string) => {
|
|
19
|
+
channelId: string;
|
|
20
|
+
context?: ParsedThreadStarted["context"] | null;
|
|
21
|
+
} | null;
|
|
22
|
+
/** Persist thread metadata in the broker DB without claiming ownership. */
|
|
23
|
+
rememberKnownThread?: (threadTs: string, channelId: string, context?: ParsedThreadStarted["context"] | null) => void;
|
|
24
|
+
/** Best-effort callback for Home tab opens. */
|
|
25
|
+
onAppHomeOpened?: (event: ParsedAppHomeOpened) => Promise<void> | void;
|
|
26
|
+
}
|
|
27
|
+
export declare const SLACK_THREAD_CACHE_MAX_SIZE = 5000;
|
|
28
|
+
export declare const SLACK_THREAD_CACHE_TTL_MS: number;
|
|
29
|
+
export declare const SLACK_PENDING_ATTENTION_MAX_THREADS = 1000;
|
|
30
|
+
export declare const SLACK_PENDING_ATTENTION_TTL_MS: number;
|
|
31
|
+
export declare const SLACK_PENDING_ATTENTION_MAX_MESSAGES_PER_THREAD = 50;
|
|
32
|
+
export declare class SlackAdapter implements MessageAdapter {
|
|
33
|
+
readonly name = "slack";
|
|
34
|
+
private readonly config;
|
|
35
|
+
private readonly allowlist;
|
|
36
|
+
private slackRequests;
|
|
37
|
+
private botUserId;
|
|
38
|
+
private socketMode;
|
|
39
|
+
private shuttingDown;
|
|
40
|
+
private inboundHandler;
|
|
41
|
+
private readonly reactionCommands;
|
|
42
|
+
private readonly threads;
|
|
43
|
+
private readonly userNames;
|
|
44
|
+
private readonly processedSocketDeliveries;
|
|
45
|
+
private readonly pendingEyes;
|
|
46
|
+
private readonly threadStatuses;
|
|
47
|
+
constructor(config: SlackAdapterConfig);
|
|
48
|
+
private callSlack;
|
|
49
|
+
connect(): Promise<void>;
|
|
50
|
+
disconnect(): Promise<void>;
|
|
51
|
+
onInbound(handler: (msg: InboundMessage) => void): void;
|
|
52
|
+
invokeCapability(request: AdapterCapabilityRequest): Promise<AdapterCapabilityResult>;
|
|
53
|
+
send(msg: OutboundMessage): Promise<void>;
|
|
54
|
+
private buildSlackApiCapabilityEffects;
|
|
55
|
+
getBotUserId(): string | null;
|
|
56
|
+
getTrackedThreadIds(): Set<string>;
|
|
57
|
+
isConnected(): boolean;
|
|
58
|
+
private resolveScopeForThread;
|
|
59
|
+
private onThreadStarted;
|
|
60
|
+
private onContextChanged;
|
|
61
|
+
private onAppHomeOpened;
|
|
62
|
+
private fetchMessageByTs;
|
|
63
|
+
private onReactionAdded;
|
|
64
|
+
private onMessage;
|
|
65
|
+
private emitInteractiveInbound;
|
|
66
|
+
private onMemberJoined;
|
|
67
|
+
private shouldSuppressLegacyThreadedDm;
|
|
68
|
+
private getThread;
|
|
69
|
+
private addReaction;
|
|
70
|
+
private removeReaction;
|
|
71
|
+
private resolveUser;
|
|
72
|
+
private clearThreadStatus;
|
|
73
|
+
private setSuggestedPrompts;
|
|
74
|
+
}
|