@cuylabs/channel-slack-agent-core 0.5.1 → 0.7.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 +20 -39
- package/dist/{adapter.d.ts → adapter/index.d.ts} +7 -5
- package/dist/adapter/index.js +9 -0
- package/dist/{adapter-Cmd2C90g.d.ts → adapter-B3CI611y.d.ts} +1 -1
- package/dist/app-surface.d.ts +13 -3
- package/dist/app-surface.js +5 -7
- package/dist/app.d.ts +9 -4
- package/dist/app.js +6 -9
- package/dist/artifacts/index.d.ts +57 -0
- package/dist/artifacts/index.js +6 -0
- package/dist/{assistant.d.ts → assistant/index.d.ts} +6 -4
- package/dist/{assistant.js → assistant/index.js} +3 -5
- package/dist/{chunk-CYEBGC6G.js → chunk-76SRS54H.js} +5 -5
- package/dist/{chunk-M64Z6TYL.js → chunk-7DUO5BMW.js} +16 -6
- package/dist/{chunk-NIPAN4KA.js → chunk-A2PLAVW6.js} +2 -3
- package/dist/chunk-C7CHMYV6.js +226 -0
- package/dist/chunk-C7VSW4ZM.js +548 -0
- package/dist/{chunk-JMLB7A2V.js → chunk-DJPKRKGP.js} +5 -5
- package/dist/chunk-ELR6MQD7.js +12 -0
- package/dist/{chunk-FDRQOG7Q.js → chunk-FQWFB54C.js} +26 -15
- package/dist/{chunk-BFUPAJON.js → chunk-MGBNGG4D.js} +59 -37
- package/dist/chunk-NNCVHQC4.js +94 -0
- package/dist/chunk-P7PFQ3SQ.js +396 -0
- package/dist/{chunk-WO4BJMF3.js → chunk-TCNJY7QA.js} +5 -5
- package/dist/{chunk-DHPD4XH5.js → chunk-TMADMHBN.js} +210 -29
- package/dist/{chunk-IWUYIAY5.js → chunk-VMVQIDNR.js} +5 -7
- package/dist/{chunk-IXY3BXU5.js → chunk-X7ILLZZP.js} +359 -2
- package/dist/context-fragments-CQEDcjYR.d.ts +30 -0
- package/dist/express-assistant.d.ts +6 -3
- package/dist/express-assistant.js +4 -7
- package/dist/express.d.ts +7 -3
- 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 +61 -0
- package/dist/history/index.js +8 -0
- package/dist/index.d.ts +23 -16
- package/dist/index.js +81 -160
- package/dist/interactive/index.d.ts +71 -0
- package/dist/{interactive.js → interactive/index.js} +9 -4
- package/dist/mcp.js +0 -1
- package/dist/{options-C7OYeNR-.d.ts → options-BcDReOJv.d.ts} +48 -0
- package/dist/{options-Uf-qmQKN.d.ts → options-CdqBABcM.d.ts} +26 -1
- package/dist/{shared.d.ts → shared/index.d.ts} +24 -36
- package/dist/{shared.js → shared/index.js} +2 -6
- package/dist/socket.d.ts +9 -4
- package/dist/socket.js +6 -9
- package/dist/source/index.d.ts +154 -0
- package/dist/source/index.js +38 -0
- package/dist/{types-BqRzb_Cd.d.ts → types-CRWzJB5G.d.ts} +35 -0
- package/dist/types-CiwGU6zC.d.ts +56 -0
- package/dist/views/index.d.ts +8 -0
- package/dist/views/index.js +10 -0
- package/docs/README.md +18 -0
- package/docs/concepts/final-response-artifacts.md +39 -0
- package/docs/concepts/interactive-requests.md +43 -0
- package/docs/concepts/tool-task-rendering.md +46 -0
- package/docs/concepts/view-workflows.md +52 -0
- package/docs/reference/boundary.md +22 -0
- package/docs/reference/exports.md +26 -0
- package/package.json +36 -50
- 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/interactive.d.ts +0 -30
- 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
|
@@ -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
|
+
};
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createSlackAssistantBridge
|
|
3
|
-
} from "./chunk-
|
|
4
|
-
import {
|
|
5
|
-
bolt_exports
|
|
6
|
-
} from "./chunk-ANIZ5NT4.js";
|
|
3
|
+
} from "./chunk-MGBNGG4D.js";
|
|
7
4
|
|
|
8
5
|
// src/express-assistant.ts
|
|
6
|
+
import {
|
|
7
|
+
createSlackBoltApp
|
|
8
|
+
} from "@cuylabs/channel-slack/transports/http";
|
|
9
9
|
async function mountSlackAssistantAgent(options) {
|
|
10
10
|
const {
|
|
11
11
|
botToken,
|
|
@@ -29,7 +29,7 @@ async function mountSlackAssistantAgent(options) {
|
|
|
29
29
|
app: expressApp,
|
|
30
30
|
authMode,
|
|
31
31
|
routePath
|
|
32
|
-
} = await
|
|
32
|
+
} = await createSlackBoltApp({
|
|
33
33
|
signingSecret,
|
|
34
34
|
path,
|
|
35
35
|
botToken,
|
|
@@ -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,22 +1,25 @@
|
|
|
1
|
+
import {
|
|
2
|
+
UnsupportedSlackInteractiveRequestError,
|
|
3
|
+
bridgeAgentEventsToSlack,
|
|
4
|
+
resolveSlackEventBridgeOptions
|
|
5
|
+
} from "./chunk-TMADMHBN.js";
|
|
6
|
+
|
|
7
|
+
// src/adapter/adapter.ts
|
|
8
|
+
import { withinScope } from "@cuylabs/agent-core";
|
|
1
9
|
import {
|
|
2
10
|
extractSlackAuthContext,
|
|
3
11
|
extractSlackUserIdentity,
|
|
4
12
|
isProcessableMessage,
|
|
5
13
|
parseSlackMentionActivity,
|
|
6
14
|
parseSlackMessageActivity,
|
|
7
|
-
resolveSlackMessageFormatter
|
|
8
|
-
} from "./chunk-GNXWTKQ6.js";
|
|
9
|
-
import {
|
|
10
|
-
UnsupportedSlackInteractiveRequestError,
|
|
11
|
-
bridgeAgentEventsToSlack,
|
|
12
|
-
resolveSlackEventBridgeOptions,
|
|
15
|
+
resolveSlackMessageFormatter,
|
|
13
16
|
runWithSlackTurnContext
|
|
14
|
-
} from "
|
|
15
|
-
|
|
16
|
-
// src/shared/session.ts
|
|
17
|
-
import { resolveThreadAwareSlackSessionId } from "@cuylabs/channel-slack/core";
|
|
17
|
+
} from "@cuylabs/channel-slack/core";
|
|
18
18
|
|
|
19
19
|
// src/adapter/session-map.ts
|
|
20
|
+
import {
|
|
21
|
+
resolveThreadAwareSlackSessionId
|
|
22
|
+
} from "@cuylabs/channel-slack/core";
|
|
20
23
|
function createSlackSessionMap(options) {
|
|
21
24
|
const strategy = options.sessionStrategy ?? "thread-aware";
|
|
22
25
|
if (strategy === "custom") {
|
|
@@ -61,9 +64,6 @@ function createSlackSessionMap(options) {
|
|
|
61
64
|
};
|
|
62
65
|
}
|
|
63
66
|
|
|
64
|
-
// src/adapter/adapter.ts
|
|
65
|
-
import { withinScope } from "@cuylabs/agent-core";
|
|
66
|
-
|
|
67
67
|
// src/adapter/sink.ts
|
|
68
68
|
function buildThreadPayload(info, respondInThread) {
|
|
69
69
|
if (!respondInThread || info.channelType === "dm") return {};
|
|
@@ -73,6 +73,11 @@ function buildThreadPayload(info, respondInThread) {
|
|
|
73
73
|
function buildResponseSink(say, client, info, respondInThread, chatStreamStartArgs, sayStream) {
|
|
74
74
|
const threadPayload = buildThreadPayload(info, respondInThread);
|
|
75
75
|
return {
|
|
76
|
+
artifactClient: client,
|
|
77
|
+
artifactTarget: {
|
|
78
|
+
channelId: info.channelId,
|
|
79
|
+
...threadPayload.thread_ts ? { threadTs: threadPayload.thread_ts } : {}
|
|
80
|
+
},
|
|
76
81
|
async postMessage(text) {
|
|
77
82
|
const result = await say({ text, ...threadPayload });
|
|
78
83
|
const response = result;
|
|
@@ -167,6 +172,7 @@ function createSlackChannelAdapter(options) {
|
|
|
167
172
|
formatToolTitle: options.formatToolTitle,
|
|
168
173
|
formatToolUpdate: options.formatToolUpdate,
|
|
169
174
|
formatToolDetails: options.formatToolDetails,
|
|
175
|
+
formatToolResultOutput: options.formatToolResultOutput,
|
|
170
176
|
formatToolError: options.formatToolError,
|
|
171
177
|
formatReasoningUpdate: options.formatReasoningUpdate,
|
|
172
178
|
interactiveMode: options.interactiveMode,
|
|
@@ -180,7 +186,13 @@ function createSlackChannelAdapter(options) {
|
|
|
180
186
|
maxTaskUpdates: options.maxTaskUpdates,
|
|
181
187
|
maxTaskUpdateTextChars: options.maxTaskUpdateTextChars,
|
|
182
188
|
maxTaskUpdateFieldChars: options.maxTaskUpdateFieldChars,
|
|
183
|
-
chatStreamFinalArgs: options.chatStreamFinalArgs
|
|
189
|
+
chatStreamFinalArgs: options.chatStreamFinalArgs,
|
|
190
|
+
publishFinalResponseArtifact: options.publishFinalResponseArtifact,
|
|
191
|
+
finalResponseArtifactMode: options.finalResponseArtifactMode,
|
|
192
|
+
finalResponseArtifactStreamThreshold: options.finalResponseArtifactStreamThreshold,
|
|
193
|
+
formatFinalResponseArtifactContinuationNotice: options.formatFinalResponseArtifactContinuationNotice,
|
|
194
|
+
formatFinalResponseArtifactMessage: options.formatFinalResponseArtifactMessage,
|
|
195
|
+
onFinalResponseArtifactError: options.onFinalResponseArtifactError
|
|
184
196
|
}
|
|
185
197
|
);
|
|
186
198
|
const timeout = options.timeout ?? 12e4;
|
|
@@ -465,7 +477,6 @@ function formatErrorForLog(error) {
|
|
|
465
477
|
}
|
|
466
478
|
|
|
467
479
|
export {
|
|
468
|
-
resolveThreadAwareSlackSessionId,
|
|
469
480
|
createSlackSessionMap,
|
|
470
481
|
createSlackChannelAdapter
|
|
471
482
|
};
|