@cuylabs/channel-slack-agent-core 0.5.1 → 0.6.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 +14 -40
- package/dist/{adapter.d.ts → adapter/index.d.ts} +4 -5
- package/dist/adapter/index.js +9 -0
- package/dist/app-surface.js +5 -7
- package/dist/app.d.ts +2 -1
- package/dist/app.js +6 -9
- package/dist/{assistant.d.ts → assistant/index.d.ts} +4 -4
- package/dist/{assistant.js → assistant/index.js} +3 -5
- package/dist/{chunk-JMLB7A2V.js → chunk-5NQYLOAW.js} +5 -5
- package/dist/{chunk-NIPAN4KA.js → chunk-A2PLAVW6.js} +2 -3
- package/dist/chunk-C7VSW4ZM.js +548 -0
- package/dist/chunk-ELR6MQD7.js +12 -0
- package/dist/{chunk-CYEBGC6G.js → chunk-FNT4TXNQ.js} +5 -5
- package/dist/{chunk-DHPD4XH5.js → chunk-NOVWLAVP.js} +1 -9
- package/dist/{chunk-M64Z6TYL.js → chunk-P7KK5GQG.js} +7 -6
- package/dist/chunk-P7PFQ3SQ.js +396 -0
- package/dist/{chunk-IWUYIAY5.js → chunk-QEJ7TAZJ.js} +5 -7
- package/dist/{chunk-FDRQOG7Q.js → chunk-TIQGJ52F.js} +13 -14
- package/dist/{chunk-WO4BJMF3.js → chunk-VCXPNQRB.js} +5 -5
- package/dist/{chunk-BFUPAJON.js → chunk-ZDVD46RT.js} +37 -37
- package/dist/context-fragments-CQEDcjYR.d.ts +30 -0
- package/dist/express-assistant.d.ts +2 -1
- package/dist/express-assistant.js +4 -7
- package/dist/express.d.ts +2 -1
- package/dist/express.js +3 -6
- package/dist/feedback/index.d.ts +1 -0
- package/dist/feedback/index.js +10 -0
- package/dist/history/index.d.ts +57 -0
- package/dist/history/index.js +8 -0
- package/dist/index.d.ts +14 -13
- package/dist/index.js +63 -160
- package/dist/{interactive.d.ts → interactive/index.d.ts} +3 -3
- package/dist/{interactive.js → interactive/index.js} +1 -2
- package/dist/mcp.js +0 -1
- package/dist/{shared.d.ts → shared/index.d.ts} +7 -33
- package/dist/{shared.js → shared/index.js} +2 -6
- package/dist/socket.d.ts +2 -1
- package/dist/socket.js +6 -9
- package/dist/source/index.d.ts +154 -0
- package/dist/source/index.js +38 -0
- package/docs/README.md +11 -0
- package/docs/reference/boundary.md +22 -0
- package/docs/reference/exports.md +24 -0
- package/package.json +27 -51
- package/dist/adapter.js +0 -13
- package/dist/bolt.d.ts +0 -8
- package/dist/bolt.js +0 -10
- package/dist/chunk-2SUAW6MV.js +0 -12
- package/dist/chunk-645NNJIM.js +0 -12
- package/dist/chunk-ANIZ5NT4.js +0 -12
- package/dist/chunk-GNXWTKQ6.js +0 -48
- package/dist/chunk-HFT2FXJP.js +0 -12
- package/dist/chunk-I2KLQ2HA.js +0 -22
- package/dist/chunk-K2E6A377.js +0 -12
- package/dist/chunk-NDVXBI7Z.js +0 -12
- package/dist/chunk-PX4RGO3N.js +0 -12
- package/dist/chunk-VHGV66M7.js +0 -12
- package/dist/diagnostics.d.ts +0 -1
- package/dist/diagnostics.js +0 -10
- package/dist/feedback.d.ts +0 -1
- package/dist/feedback.js +0 -10
- package/dist/history.d.ts +0 -1
- package/dist/history.js +0 -10
- package/dist/policy.d.ts +0 -1
- package/dist/policy.js +0 -10
- package/dist/setup.d.ts +0 -1
- package/dist/setup.js +0 -10
- package/dist/targets.d.ts +0 -1
- package/dist/targets.js +0 -10
- package/dist/users.d.ts +0 -1
- package/dist/users.js +0 -10
- /package/dist/{chunk-IXY3BXU5.js → chunk-J6CW2RGO.js} +0 -0
|
@@ -0,0 +1,548 @@
|
|
|
1
|
+
// src/source/event-queue.ts
|
|
2
|
+
function coalesceSlackAgentEvents(previous, next) {
|
|
3
|
+
if (previous.type === "text-delta" && next.type === "text-delta") {
|
|
4
|
+
return {
|
|
5
|
+
type: "text-delta",
|
|
6
|
+
text: previous.text + next.text
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
return void 0;
|
|
10
|
+
}
|
|
11
|
+
function routeSlackAgentEvent({
|
|
12
|
+
event,
|
|
13
|
+
logger,
|
|
14
|
+
onRecord,
|
|
15
|
+
queue,
|
|
16
|
+
sequence,
|
|
17
|
+
state
|
|
18
|
+
}) {
|
|
19
|
+
if (sequence !== void 0) {
|
|
20
|
+
if (state.deliveredSequences.has(sequence)) {
|
|
21
|
+
logger?.debug?.("Skipping duplicate Slack agent event", {
|
|
22
|
+
eventType: event.type,
|
|
23
|
+
sequence,
|
|
24
|
+
sessionId: state.sessionId,
|
|
25
|
+
turnId: state.turnId
|
|
26
|
+
});
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
state.deliveredSequences.add(sequence);
|
|
30
|
+
}
|
|
31
|
+
onRecord?.(event, sequence);
|
|
32
|
+
if (!shouldQueueSlackAgentEvent({ event, logger, sequence, state })) {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
queue.push(event, coalesceSlackAgentEvents);
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
function shouldQueueSlackAgentEvent({
|
|
39
|
+
event,
|
|
40
|
+
logger,
|
|
41
|
+
sequence,
|
|
42
|
+
state
|
|
43
|
+
}) {
|
|
44
|
+
const status = slackAgentEventStatusKey(event);
|
|
45
|
+
if (!status) {
|
|
46
|
+
if (event.type === "subagent-complete" || event.type === "subagent-error") {
|
|
47
|
+
state.lastQueuedStatusByKey.delete(`subagent:${event.dispatchId}`);
|
|
48
|
+
}
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
const previous = state.lastQueuedStatusByKey.get(status.key);
|
|
52
|
+
if (previous === status.status) {
|
|
53
|
+
logger?.debug?.("Skipping duplicate Slack status event", {
|
|
54
|
+
eventType: event.type,
|
|
55
|
+
sequence,
|
|
56
|
+
sessionId: state.sessionId,
|
|
57
|
+
status: status.status,
|
|
58
|
+
statusKey: status.key,
|
|
59
|
+
turnId: state.turnId
|
|
60
|
+
});
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
state.lastQueuedStatusByKey.set(status.key, status.status);
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
function slackAgentEventStatusKey(event) {
|
|
67
|
+
if (event.type === "status") {
|
|
68
|
+
return { key: "root", status: event.status };
|
|
69
|
+
}
|
|
70
|
+
if (event.type === "subagent-event" && event.event.type === "status") {
|
|
71
|
+
return {
|
|
72
|
+
key: `subagent:${event.dispatchId}`,
|
|
73
|
+
status: event.event.status
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
return void 0;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// src/source/responses.ts
|
|
80
|
+
async function* immediateSlackTextResponse(text) {
|
|
81
|
+
yield { type: "text-start" };
|
|
82
|
+
yield { type: "text-delta", text };
|
|
83
|
+
yield { type: "text-end" };
|
|
84
|
+
yield { type: "complete", output: text };
|
|
85
|
+
}
|
|
86
|
+
function isSlackCancelMessage(message) {
|
|
87
|
+
const normalized = message.trim().toLowerCase();
|
|
88
|
+
return /^(cancel|stop|abort|nevermind|never mind|discard|quit)\b[.!?]*$/.test(
|
|
89
|
+
normalized
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
function resolveSlackTypedApprovalAction(message) {
|
|
93
|
+
const normalized = message.trim().toLowerCase();
|
|
94
|
+
if (/^(allow|approve|approved|yes|y)\b[.!?]*$/.test(normalized)) {
|
|
95
|
+
return "allow";
|
|
96
|
+
}
|
|
97
|
+
if (/^(deny|denied|no|n)\b[.!?]*$/.test(normalized)) {
|
|
98
|
+
return "deny";
|
|
99
|
+
}
|
|
100
|
+
if (/^(remember|always|allow always|approve always)\b[.!?]*$/.test(normalized)) {
|
|
101
|
+
return "remember";
|
|
102
|
+
}
|
|
103
|
+
return void 0;
|
|
104
|
+
}
|
|
105
|
+
function isRunningAgentTurnError(error) {
|
|
106
|
+
return error instanceof Error && /already has a running turn/i.test(error.message);
|
|
107
|
+
}
|
|
108
|
+
function isAbortLikeError(error) {
|
|
109
|
+
if (error instanceof DOMException) {
|
|
110
|
+
return error.name === "AbortError";
|
|
111
|
+
}
|
|
112
|
+
if (error instanceof Error) {
|
|
113
|
+
return error.name === "AbortError" || /aborted/i.test(error.message);
|
|
114
|
+
}
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// src/source/status-visibility.ts
|
|
119
|
+
function resolveSlackTurnStatusVisibilityOptions(options) {
|
|
120
|
+
return {
|
|
121
|
+
checkIntervalMs: Math.max(1, normalizeMs(options?.checkIntervalMs, 5e3)),
|
|
122
|
+
toolStaleWarningMs: normalizeMs(options?.toolStaleWarningMs, 0),
|
|
123
|
+
turnStaleWarningMs: normalizeMs(options?.turnStaleWarningMs, 0)
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
function shouldInspectSlackTurnStatusVisibility(options) {
|
|
127
|
+
return options.toolStaleWarningMs > 0 || options.turnStaleWarningMs > 0;
|
|
128
|
+
}
|
|
129
|
+
function recordSlackTurnActivity(state, event, nowMs = Date.now()) {
|
|
130
|
+
state.lastActivityAtMs = nowMs;
|
|
131
|
+
switch (event.type) {
|
|
132
|
+
case "text-start":
|
|
133
|
+
case "text-delta":
|
|
134
|
+
case "text-end":
|
|
135
|
+
state.phase = "streaming";
|
|
136
|
+
state.currentTool = void 0;
|
|
137
|
+
break;
|
|
138
|
+
case "reasoning-start":
|
|
139
|
+
case "reasoning-delta":
|
|
140
|
+
case "reasoning-end":
|
|
141
|
+
case "message":
|
|
142
|
+
case "step-start":
|
|
143
|
+
case "step-finish":
|
|
144
|
+
case "status":
|
|
145
|
+
case "turn-boundary":
|
|
146
|
+
if (!isSlackTerminalTurnPhase(state.phase)) {
|
|
147
|
+
state.phase = "reasoning";
|
|
148
|
+
}
|
|
149
|
+
break;
|
|
150
|
+
case "tool-start":
|
|
151
|
+
state.phase = "tool-running";
|
|
152
|
+
state.currentTool = {
|
|
153
|
+
startedAtMs: nowMs,
|
|
154
|
+
toolCallId: event.toolCallId,
|
|
155
|
+
toolName: event.toolName
|
|
156
|
+
};
|
|
157
|
+
break;
|
|
158
|
+
case "tool-result":
|
|
159
|
+
case "tool-error":
|
|
160
|
+
if (state.currentTool?.toolCallId === event.toolCallId) {
|
|
161
|
+
state.currentTool = void 0;
|
|
162
|
+
}
|
|
163
|
+
if (!isSlackTerminalTurnPhase(state.phase)) {
|
|
164
|
+
state.phase = "reasoning";
|
|
165
|
+
}
|
|
166
|
+
break;
|
|
167
|
+
case "approval-request":
|
|
168
|
+
state.currentTool = void 0;
|
|
169
|
+
state.phase = "waiting-approval";
|
|
170
|
+
break;
|
|
171
|
+
case "approval-resolved":
|
|
172
|
+
if (!isSlackTerminalTurnPhase(state.phase)) {
|
|
173
|
+
state.phase = "reasoning";
|
|
174
|
+
}
|
|
175
|
+
break;
|
|
176
|
+
case "human-input-request":
|
|
177
|
+
state.currentTool = void 0;
|
|
178
|
+
state.phase = "waiting-input";
|
|
179
|
+
break;
|
|
180
|
+
case "human-input-resolved":
|
|
181
|
+
if (!isSlackTerminalTurnPhase(state.phase)) {
|
|
182
|
+
state.phase = "reasoning";
|
|
183
|
+
}
|
|
184
|
+
break;
|
|
185
|
+
case "complete":
|
|
186
|
+
state.currentTool = void 0;
|
|
187
|
+
state.phase = "completed";
|
|
188
|
+
break;
|
|
189
|
+
case "error":
|
|
190
|
+
state.currentTool = void 0;
|
|
191
|
+
state.phase = "failed";
|
|
192
|
+
break;
|
|
193
|
+
default:
|
|
194
|
+
break;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
function inspectSlackTurnStatusVisibility({
|
|
198
|
+
formatToolStaleStatus = defaultToolStaleStatus,
|
|
199
|
+
formatTurnStaleStatus = defaultTurnStaleStatus,
|
|
200
|
+
nowMs = Date.now(),
|
|
201
|
+
options,
|
|
202
|
+
state
|
|
203
|
+
}) {
|
|
204
|
+
if (isSlackTerminalTurnPhase(state.phase) || isSlackWaitingForHumanTurnPhase(state.phase)) {
|
|
205
|
+
return void 0;
|
|
206
|
+
}
|
|
207
|
+
if (state.currentTool && options.toolStaleWarningMs > 0) {
|
|
208
|
+
const elapsedMs = nowMs - state.currentTool.startedAtMs;
|
|
209
|
+
if (elapsedMs >= options.toolStaleWarningMs) {
|
|
210
|
+
return createSlackTurnStatusVisibilityWarning({
|
|
211
|
+
id: `tool:${state.currentTool.toolCallId}`,
|
|
212
|
+
message: formatToolStaleStatus(state.currentTool.toolName),
|
|
213
|
+
nowMs,
|
|
214
|
+
state,
|
|
215
|
+
thresholdMs: options.toolStaleWarningMs,
|
|
216
|
+
type: "tool"
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
if (options.turnStaleWarningMs <= 0) {
|
|
221
|
+
return void 0;
|
|
222
|
+
}
|
|
223
|
+
const idleMs = nowMs - state.lastActivityAtMs;
|
|
224
|
+
if (idleMs < options.turnStaleWarningMs) {
|
|
225
|
+
return void 0;
|
|
226
|
+
}
|
|
227
|
+
return createSlackTurnStatusVisibilityWarning({
|
|
228
|
+
id: `turn:${state.phase}`,
|
|
229
|
+
message: formatTurnStaleStatus(state.phase),
|
|
230
|
+
nowMs,
|
|
231
|
+
state,
|
|
232
|
+
thresholdMs: options.turnStaleWarningMs,
|
|
233
|
+
type: "turn"
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
function isSlackWaitingForHumanTurnPhase(phase) {
|
|
237
|
+
return phase === "waiting-approval" || phase === "waiting-input";
|
|
238
|
+
}
|
|
239
|
+
function isSlackTerminalTurnPhase(phase) {
|
|
240
|
+
return phase === "completed" || phase === "failed" || phase === "cancelled";
|
|
241
|
+
}
|
|
242
|
+
function createSlackTurnStatusVisibilityWarning({
|
|
243
|
+
id,
|
|
244
|
+
message,
|
|
245
|
+
nowMs,
|
|
246
|
+
state,
|
|
247
|
+
thresholdMs,
|
|
248
|
+
type
|
|
249
|
+
}) {
|
|
250
|
+
if (state.statusWarningsSent.has(id)) {
|
|
251
|
+
return void 0;
|
|
252
|
+
}
|
|
253
|
+
state.statusWarningsSent.add(id);
|
|
254
|
+
const receivedEventCount = Object.values(state.eventCounts).reduce(
|
|
255
|
+
(total, count) => total + count,
|
|
256
|
+
0
|
|
257
|
+
);
|
|
258
|
+
const idleMs = type === "tool" && state.currentTool ? nowMs - state.currentTool.startedAtMs : nowMs - state.lastActivityAtMs;
|
|
259
|
+
return {
|
|
260
|
+
elapsedMs: nowMs - state.startedAtMs,
|
|
261
|
+
...state.firstEventAtMs === void 0 ? {} : { firstEventLatencyMs: state.firstEventAtMs - state.startedAtMs },
|
|
262
|
+
...state.firstEventType ? { firstEventType: state.firstEventType } : {},
|
|
263
|
+
id,
|
|
264
|
+
idleMs,
|
|
265
|
+
message,
|
|
266
|
+
phase: state.phase,
|
|
267
|
+
receivedEventCount,
|
|
268
|
+
...state.sessionId ? { sessionId: state.sessionId } : {},
|
|
269
|
+
staleBeforeFirstEvent: receivedEventCount === 0,
|
|
270
|
+
thresholdMs,
|
|
271
|
+
...state.currentTool ? {
|
|
272
|
+
toolCallId: state.currentTool.toolCallId,
|
|
273
|
+
toolName: state.currentTool.toolName
|
|
274
|
+
} : {},
|
|
275
|
+
...state.turnId ? { turnId: state.turnId } : {},
|
|
276
|
+
type
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
function defaultToolStaleStatus(toolName) {
|
|
280
|
+
return `Still waiting on ${toolName}...`;
|
|
281
|
+
}
|
|
282
|
+
function defaultTurnStaleStatus(phase) {
|
|
283
|
+
switch (phase) {
|
|
284
|
+
case "streaming":
|
|
285
|
+
return "The response stream is taking longer than expected. I am still working on it.";
|
|
286
|
+
case "tool-running":
|
|
287
|
+
return "A tool is taking longer than expected. I am still waiting for it.";
|
|
288
|
+
default:
|
|
289
|
+
return "This request is taking longer than expected. I am still working on it.";
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
function normalizeMs(value, fallback) {
|
|
293
|
+
if (value === void 0 || !Number.isFinite(value) || value < 0) {
|
|
294
|
+
return fallback;
|
|
295
|
+
}
|
|
296
|
+
return Math.floor(value);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// src/source/subagent-completion-notifier.ts
|
|
300
|
+
function createSlackSubagentCompletionNotifier({
|
|
301
|
+
enabled = true,
|
|
302
|
+
formatMessage = formatDefaultSlackSubagentCompletionMessage,
|
|
303
|
+
logger,
|
|
304
|
+
postMessage: initialPostMessage
|
|
305
|
+
} = {}) {
|
|
306
|
+
const runningByDispatchId = /* @__PURE__ */ new Map();
|
|
307
|
+
const detachedTurnIds = /* @__PURE__ */ new Set();
|
|
308
|
+
const deliveredInlineDispatchIds = /* @__PURE__ */ new Set();
|
|
309
|
+
const postedTerminalKeys = /* @__PURE__ */ new Set();
|
|
310
|
+
let postMessage = initialPostMessage;
|
|
311
|
+
function trackStartedRun(turn, event) {
|
|
312
|
+
if (!enabled || deliveredInlineDispatchIds.has(event.dispatchId)) {
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
const slack = slackThreadContext(turn.slack);
|
|
316
|
+
if (!slack) {
|
|
317
|
+
logger?.debug?.("Skipping subagent Slack notification tracking", {
|
|
318
|
+
dispatchId: event.dispatchId,
|
|
319
|
+
reason: "missing-slack-thread-context",
|
|
320
|
+
sessionId: turn.sessionId,
|
|
321
|
+
turnId: turn.turnId
|
|
322
|
+
});
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
runningByDispatchId.set(event.dispatchId, {
|
|
326
|
+
dispatchId: event.dispatchId,
|
|
327
|
+
role: event.role,
|
|
328
|
+
sessionId: turn.sessionId,
|
|
329
|
+
slack,
|
|
330
|
+
title: event.title,
|
|
331
|
+
turnId: turn.turnId
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
function markResultDeliveredInline(event) {
|
|
335
|
+
deliveredInlineDispatchIds.add(event.dispatchId);
|
|
336
|
+
runningByDispatchId.delete(event.dispatchId);
|
|
337
|
+
}
|
|
338
|
+
function markTurnDetached(turnId) {
|
|
339
|
+
detachedTurnIds.add(turnId);
|
|
340
|
+
}
|
|
341
|
+
async function postDetachedCompletionsFromEventStore(eventStore) {
|
|
342
|
+
if (!enabled || runningByDispatchId.size === 0) {
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
let records;
|
|
346
|
+
try {
|
|
347
|
+
records = await eventStore.list({
|
|
348
|
+
type: ["subagent-complete", "subagent-error"]
|
|
349
|
+
});
|
|
350
|
+
} catch (error) {
|
|
351
|
+
logger?.warn?.(
|
|
352
|
+
"Failed to inspect dispatch events for Slack notifications",
|
|
353
|
+
{
|
|
354
|
+
error: formatNotifierError(error)
|
|
355
|
+
}
|
|
356
|
+
);
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
for (const record of records) {
|
|
360
|
+
if (!isSlackSubagentTerminalEvent(record.event)) {
|
|
361
|
+
continue;
|
|
362
|
+
}
|
|
363
|
+
const run = runningByDispatchId.get(record.dispatchId);
|
|
364
|
+
if (!run || !detachedTurnIds.has(run.turnId)) {
|
|
365
|
+
continue;
|
|
366
|
+
}
|
|
367
|
+
await postTerminalEvent(record.event);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
async function postTerminalEvent(event) {
|
|
371
|
+
if (!enabled || deliveredInlineDispatchIds.has(event.dispatchId)) {
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
const key = `${event.dispatchId}:${event.type}`;
|
|
375
|
+
if (postedTerminalKeys.has(key)) {
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
const run = runningByDispatchId.get(event.dispatchId);
|
|
379
|
+
if (!run) {
|
|
380
|
+
logger?.debug?.("Skipping detached subagent Slack notification", {
|
|
381
|
+
dispatchId: event.dispatchId,
|
|
382
|
+
eventType: event.type,
|
|
383
|
+
reason: "missing-tracked-start"
|
|
384
|
+
});
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
387
|
+
if (!postMessage) {
|
|
388
|
+
logger?.debug?.("Skipping detached subagent Slack notification", {
|
|
389
|
+
dispatchId: event.dispatchId,
|
|
390
|
+
eventType: event.type,
|
|
391
|
+
reason: "missing-slack-poster"
|
|
392
|
+
});
|
|
393
|
+
return;
|
|
394
|
+
}
|
|
395
|
+
try {
|
|
396
|
+
await postMessage(formatMessage({ event, run }));
|
|
397
|
+
postedTerminalKeys.add(key);
|
|
398
|
+
runningByDispatchId.delete(event.dispatchId);
|
|
399
|
+
logger?.info?.("Slack subagent completion notification sent", {
|
|
400
|
+
channelId: run.slack.channelId,
|
|
401
|
+
dispatchId: event.dispatchId,
|
|
402
|
+
eventType: event.type,
|
|
403
|
+
sessionId: run.sessionId,
|
|
404
|
+
threadTs: run.slack.threadTs,
|
|
405
|
+
turnId: run.turnId
|
|
406
|
+
});
|
|
407
|
+
} catch (error) {
|
|
408
|
+
logger?.warn?.("Failed to send Slack subagent completion notification", {
|
|
409
|
+
dispatchId: event.dispatchId,
|
|
410
|
+
error: formatNotifierError(error),
|
|
411
|
+
eventType: event.type,
|
|
412
|
+
sessionId: run.sessionId,
|
|
413
|
+
turnId: run.turnId
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
return {
|
|
418
|
+
clear() {
|
|
419
|
+
runningByDispatchId.clear();
|
|
420
|
+
detachedTurnIds.clear();
|
|
421
|
+
deliveredInlineDispatchIds.clear();
|
|
422
|
+
postedTerminalKeys.clear();
|
|
423
|
+
},
|
|
424
|
+
markResultDeliveredInline,
|
|
425
|
+
markTurnDetached,
|
|
426
|
+
postDetachedCompletionsFromEventStore,
|
|
427
|
+
postTerminalEvent,
|
|
428
|
+
setPostMessage(nextPostMessage) {
|
|
429
|
+
postMessage = nextPostMessage;
|
|
430
|
+
},
|
|
431
|
+
trackStartedRun
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
function isSlackSubagentTerminalEvent(event) {
|
|
435
|
+
return event.type === "subagent-complete" || event.type === "subagent-error";
|
|
436
|
+
}
|
|
437
|
+
function formatDefaultSlackSubagentCompletionMessage({
|
|
438
|
+
event,
|
|
439
|
+
run
|
|
440
|
+
}) {
|
|
441
|
+
const roleTitle = formatRoleTitle(event.role || run.role);
|
|
442
|
+
const title = event.title || run.title || roleTitle;
|
|
443
|
+
const dispatchId = event.dispatchId || run.dispatchId;
|
|
444
|
+
const completed = event.type === "subagent-complete";
|
|
445
|
+
const statusText = completed ? "Result ready" : event.status === "cancelled" ? "Cancelled" : "Failed";
|
|
446
|
+
const heading = completed ? "Subagent result ready" : "Subagent stopped";
|
|
447
|
+
const instruction = completed ? `Ask me to retrieve the result for subagent \`${escapeCode(dispatchId)}\`.` : `The subagent did not finish successfully: ${event.error}`;
|
|
448
|
+
const fallback = [
|
|
449
|
+
`${roleTitle} ${statusText.toLowerCase()}: ${title}`,
|
|
450
|
+
"",
|
|
451
|
+
`Subagent ID: ${dispatchId}`,
|
|
452
|
+
instruction
|
|
453
|
+
].join("\n");
|
|
454
|
+
return {
|
|
455
|
+
channel: run.slack.channelId,
|
|
456
|
+
thread_ts: run.slack.threadTs,
|
|
457
|
+
text: fallback,
|
|
458
|
+
blocks: [
|
|
459
|
+
{
|
|
460
|
+
type: "section",
|
|
461
|
+
text: {
|
|
462
|
+
type: "mrkdwn",
|
|
463
|
+
text: `*${escapeMrkdwn(heading)}*`
|
|
464
|
+
}
|
|
465
|
+
},
|
|
466
|
+
{ type: "divider" },
|
|
467
|
+
{
|
|
468
|
+
type: "section",
|
|
469
|
+
fields: [
|
|
470
|
+
{
|
|
471
|
+
type: "mrkdwn",
|
|
472
|
+
text: `*Subagent*
|
|
473
|
+
${escapeMrkdwn(roleTitle)}`
|
|
474
|
+
},
|
|
475
|
+
{
|
|
476
|
+
type: "mrkdwn",
|
|
477
|
+
text: `*Status*
|
|
478
|
+
${escapeMrkdwn(statusText)}`
|
|
479
|
+
},
|
|
480
|
+
{
|
|
481
|
+
type: "mrkdwn",
|
|
482
|
+
text: `*Task*
|
|
483
|
+
${escapeMrkdwn(title)}`
|
|
484
|
+
},
|
|
485
|
+
{
|
|
486
|
+
type: "mrkdwn",
|
|
487
|
+
text: `*Subagent ID*
|
|
488
|
+
\`${escapeCode(dispatchId)}\``
|
|
489
|
+
}
|
|
490
|
+
]
|
|
491
|
+
},
|
|
492
|
+
{
|
|
493
|
+
type: "context",
|
|
494
|
+
elements: [
|
|
495
|
+
{
|
|
496
|
+
type: "mrkdwn",
|
|
497
|
+
text: escapeMrkdwn(instruction)
|
|
498
|
+
}
|
|
499
|
+
]
|
|
500
|
+
}
|
|
501
|
+
]
|
|
502
|
+
};
|
|
503
|
+
}
|
|
504
|
+
function slackThreadContext(slack) {
|
|
505
|
+
if (!slack?.channelId || !slack.threadTs) {
|
|
506
|
+
return void 0;
|
|
507
|
+
}
|
|
508
|
+
return {
|
|
509
|
+
channelId: slack.channelId,
|
|
510
|
+
threadTs: slack.threadTs,
|
|
511
|
+
...slack.channelType ? { channelType: slack.channelType } : {},
|
|
512
|
+
...slack.messageTs ? { messageTs: slack.messageTs } : {},
|
|
513
|
+
...slack.teamId ? { teamId: slack.teamId } : {}
|
|
514
|
+
};
|
|
515
|
+
}
|
|
516
|
+
function formatRoleTitle(role) {
|
|
517
|
+
const words = role.replace(/[_-]+/g, " ").trim();
|
|
518
|
+
return words ? words.charAt(0).toUpperCase() + words.slice(1) : "Subagent";
|
|
519
|
+
}
|
|
520
|
+
function escapeMrkdwn(value) {
|
|
521
|
+
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
522
|
+
}
|
|
523
|
+
function escapeCode(value) {
|
|
524
|
+
return escapeMrkdwn(value).replace(/`/g, "'");
|
|
525
|
+
}
|
|
526
|
+
function formatNotifierError(error) {
|
|
527
|
+
return error instanceof Error ? error.stack ?? error.message : String(error);
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
export {
|
|
531
|
+
coalesceSlackAgentEvents,
|
|
532
|
+
routeSlackAgentEvent,
|
|
533
|
+
shouldQueueSlackAgentEvent,
|
|
534
|
+
immediateSlackTextResponse,
|
|
535
|
+
isSlackCancelMessage,
|
|
536
|
+
resolveSlackTypedApprovalAction,
|
|
537
|
+
isRunningAgentTurnError,
|
|
538
|
+
isAbortLikeError,
|
|
539
|
+
resolveSlackTurnStatusVisibilityOptions,
|
|
540
|
+
shouldInspectSlackTurnStatusVisibility,
|
|
541
|
+
recordSlackTurnActivity,
|
|
542
|
+
inspectSlackTurnStatusVisibility,
|
|
543
|
+
isSlackWaitingForHumanTurnPhase,
|
|
544
|
+
isSlackTerminalTurnPhase,
|
|
545
|
+
createSlackSubagentCompletionNotifier,
|
|
546
|
+
isSlackSubagentTerminalEvent,
|
|
547
|
+
formatDefaultSlackSubagentCompletionMessage
|
|
548
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// src/feedback/index.ts
|
|
2
|
+
import {
|
|
3
|
+
SLACK_FEEDBACK_ACTION_ID,
|
|
4
|
+
createSlackFeedbackBlock,
|
|
5
|
+
registerSlackFeedbackAction
|
|
6
|
+
} from "@cuylabs/channel-slack/feedback";
|
|
7
|
+
|
|
8
|
+
export {
|
|
9
|
+
SLACK_FEEDBACK_ACTION_ID,
|
|
10
|
+
createSlackFeedbackBlock,
|
|
11
|
+
registerSlackFeedbackAction
|
|
12
|
+
};
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
installSlackAgentAppSurface
|
|
3
|
-
} from "./chunk-
|
|
4
|
-
import {
|
|
5
|
-
bolt_exports
|
|
6
|
-
} from "./chunk-ANIZ5NT4.js";
|
|
3
|
+
} from "./chunk-P7KK5GQG.js";
|
|
7
4
|
|
|
8
5
|
// src/app.ts
|
|
6
|
+
import {
|
|
7
|
+
createSlackBoltApp
|
|
8
|
+
} from "@cuylabs/channel-slack/transports/http";
|
|
9
9
|
async function mountSlackAgentApp(options) {
|
|
10
10
|
const {
|
|
11
11
|
botToken,
|
|
@@ -27,7 +27,7 @@ async function mountSlackAgentApp(options) {
|
|
|
27
27
|
app: expressApp,
|
|
28
28
|
authMode,
|
|
29
29
|
routePath
|
|
30
|
-
} = await
|
|
30
|
+
} = await createSlackBoltApp({
|
|
31
31
|
signingSecret,
|
|
32
32
|
path,
|
|
33
33
|
botToken,
|
|
@@ -812,16 +812,8 @@ function resolveNonNegativeInteger(value, fallback) {
|
|
|
812
812
|
return Math.floor(value);
|
|
813
813
|
}
|
|
814
814
|
|
|
815
|
-
// src/shared/turn-context.ts
|
|
816
|
-
import {
|
|
817
|
-
currentSlackTurnContext,
|
|
818
|
-
runWithSlackTurnContext
|
|
819
|
-
} from "@cuylabs/channel-slack/core";
|
|
820
|
-
|
|
821
815
|
export {
|
|
822
816
|
UnsupportedSlackInteractiveRequestError,
|
|
823
817
|
bridgeAgentEventsToSlack,
|
|
824
|
-
resolveSlackEventBridgeOptions
|
|
825
|
-
currentSlackTurnContext,
|
|
826
|
-
runWithSlackTurnContext
|
|
818
|
+
resolveSlackEventBridgeOptions
|
|
827
819
|
};
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createSlackAssistantBridge
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-ZDVD46RT.js";
|
|
4
4
|
import {
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
createSlackFeedbackBlock,
|
|
6
|
+
registerSlackFeedbackAction
|
|
7
|
+
} from "./chunk-ELR6MQD7.js";
|
|
7
8
|
import {
|
|
8
9
|
createSlackChannelAdapter
|
|
9
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-TIQGJ52F.js";
|
|
10
11
|
|
|
11
12
|
// src/app-surface.ts
|
|
12
13
|
var DEFAULT_TIMEOUT_MS = 12e4;
|
|
@@ -29,7 +30,7 @@ function installSlackAgentAppSurface(boltApp, options) {
|
|
|
29
30
|
} = options;
|
|
30
31
|
const feedbackConfig = feedbackOption === false ? void 0 : feedbackOption ?? (onFeedback ? { onFeedback } : {});
|
|
31
32
|
const assistantFeedback = feedbackOption === false ? false : feedbackConfig;
|
|
32
|
-
const feedbackBlock = feedbackConfig ?
|
|
33
|
+
const feedbackBlock = feedbackConfig ? createSlackFeedbackBlock(feedbackConfig) : void 0;
|
|
33
34
|
const formatStreamError = assistantBridgeOptions.formatStreamError ?? (() => DEFAULT_STREAM_ERROR_MESSAGE);
|
|
34
35
|
const formatting = assistantBridgeOptions.formatChatMarkdown;
|
|
35
36
|
const channelSource = createErrorTranslatingSource(source, formatStreamError);
|
|
@@ -92,7 +93,7 @@ function installSlackAgentAppSurface(boltApp, options) {
|
|
|
92
93
|
channelAdapter.mount(boltApp);
|
|
93
94
|
if (feedbackConfig) {
|
|
94
95
|
const feedbackSessionStrategy = assistantBridgeOptions.sessionStrategy ?? "thread-aware";
|
|
95
|
-
|
|
96
|
+
registerSlackFeedbackAction(boltApp, {
|
|
96
97
|
...feedbackConfig,
|
|
97
98
|
resolveSessionId: (ctx) => resolveFeedbackSessionId(ctx, feedbackSessionStrategy),
|
|
98
99
|
onFeedback: feedbackConfig.onFeedback ?? (async (_ctx) => {
|