@unblocklabs/slack-subagent-card 1.0.1 → 1.0.3
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/index.ts +63 -12
- package/openclaw.plugin.json +2 -3
- package/package.json +2 -2
package/index.ts
CHANGED
|
@@ -156,8 +156,11 @@ export default definePluginEntry({
|
|
|
156
156
|
|
|
157
157
|
const web = createSlackWebClient(token);
|
|
158
158
|
|
|
159
|
+
log.info(`slack-subagent-card: registering hooks (registrationMode=${pluginApi.registrationMode}, typeof api.on=${typeof pluginApi.on})`);
|
|
160
|
+
|
|
159
161
|
pluginApi.on("subagent_spawned", async (event, ctx) => {
|
|
160
162
|
try {
|
|
163
|
+
log.info(`slack-subagent-card: subagent_spawned fired — runId=${(event as SubagentSpawnedEvent).runId ?? ctx.runId} requesterSessionKey=${ctx.requesterSessionKey} threadRequested=${(event as SubagentSpawnedEvent).threadRequested} requester=${JSON.stringify((event as SubagentSpawnedEvent).requester)}`);
|
|
161
164
|
await handleSpawned(pluginApi, web, shared, event as SubagentSpawnedEvent, ctx);
|
|
162
165
|
} catch (error) {
|
|
163
166
|
log.warn(`slack-subagent-card: subagent_spawned failed: ${stringifyError(error)}`);
|
|
@@ -166,6 +169,7 @@ export default definePluginEntry({
|
|
|
166
169
|
|
|
167
170
|
pluginApi.on("subagent_ended", async (event, ctx) => {
|
|
168
171
|
try {
|
|
172
|
+
log.info(`slack-subagent-card: subagent_ended fired — runId=${(event as SubagentEndedEvent).runId ?? ctx.runId} outcome=${(event as SubagentEndedEvent).outcome}`);
|
|
169
173
|
await handleEnded(pluginApi, web, shared, event as SubagentEndedEvent, ctx);
|
|
170
174
|
} catch (error) {
|
|
171
175
|
log.warn(`slack-subagent-card: subagent_ended failed: ${stringifyError(error)}`);
|
|
@@ -174,6 +178,7 @@ export default definePluginEntry({
|
|
|
174
178
|
|
|
175
179
|
pluginApi.on("subagent_delivery_target", async (event, ctx) => {
|
|
176
180
|
try {
|
|
181
|
+
log.info(`slack-subagent-card: subagent_delivery_target fired — runId=${(event as SubagentDeliveryTargetEvent).childRunId ?? ctx.runId}, updating card early`);
|
|
177
182
|
await handleDeliveryTarget(pluginApi, web, shared, event as SubagentDeliveryTargetEvent, ctx);
|
|
178
183
|
} catch (error) {
|
|
179
184
|
log.warn(`slack-subagent-card: subagent_delivery_target failed: ${stringifyError(error)}`);
|
|
@@ -244,7 +249,7 @@ async function handleSpawned(
|
|
|
244
249
|
|
|
245
250
|
cleanupStaleRuns(shared, api.logger);
|
|
246
251
|
|
|
247
|
-
const target = resolveSlackThreadTarget(requesterSessionKey, event.requester);
|
|
252
|
+
const target = await resolveSlackThreadTarget(requesterSessionKey, event.requester, web, api.logger);
|
|
248
253
|
if (!target) {
|
|
249
254
|
api.logger.debug?.(
|
|
250
255
|
`slack-subagent-card: no Slack thread target for runId=${runId} requesterSessionKey=${requesterSessionKey}`,
|
|
@@ -263,10 +268,10 @@ async function handleSpawned(
|
|
|
263
268
|
web.chat.postMessage({
|
|
264
269
|
channel: target.channelId,
|
|
265
270
|
thread_ts: target.threadTs,
|
|
266
|
-
text: `${CARD_TEXT_PREFIX}${cardTitle}: Running`,
|
|
271
|
+
text: `${CARD_TEXT_PREFIX}${cardTitle}: SubAgent Running`,
|
|
267
272
|
blocks: buildBlocks({
|
|
268
273
|
label: cardTitle,
|
|
269
|
-
statusText: "⏳ Running",
|
|
274
|
+
statusText: "⏳ SubAgent Running",
|
|
270
275
|
taskId: runId,
|
|
271
276
|
}) as any,
|
|
272
277
|
}),
|
|
@@ -574,21 +579,36 @@ function normalizeOutcome(value: unknown): Outcome {
|
|
|
574
579
|
return value === "error" || value === "timeout" || value === "killed" ? value : "ok";
|
|
575
580
|
}
|
|
576
581
|
|
|
577
|
-
function resolveSlackThreadTarget(
|
|
582
|
+
async function resolveSlackThreadTarget(
|
|
578
583
|
requesterSessionKey: string,
|
|
579
|
-
requester
|
|
580
|
-
|
|
584
|
+
requester: SlackRequester | undefined,
|
|
585
|
+
web: WebClient,
|
|
586
|
+
log: Logger,
|
|
587
|
+
): Promise<SlackThreadTarget | null> {
|
|
581
588
|
const fromSession = parseSlackThreadSessionKey(requesterSessionKey);
|
|
582
|
-
if (fromSession)
|
|
589
|
+
if (fromSession) {
|
|
590
|
+
// For DM sessions the captured ID is a user ID (U...), not a DM channel (D...).
|
|
591
|
+
// Slack's chat.postMessage needs the D-prefixed channel ID for thread replies.
|
|
592
|
+
if (/^U/i.test(fromSession.channelId)) {
|
|
593
|
+
const resolved = await resolveUserToDmChannel(web, fromSession.channelId, log);
|
|
594
|
+
if (!resolved) return null;
|
|
595
|
+
return { channelId: resolved, threadTs: fromSession.threadTs };
|
|
596
|
+
}
|
|
597
|
+
return fromSession;
|
|
598
|
+
}
|
|
583
599
|
|
|
584
600
|
const rawTarget = asNonEmptyString(requester?.to);
|
|
585
601
|
const threadTs = asNonEmptyString(requester?.threadId);
|
|
586
602
|
if (!rawTarget || !threadTs) return null;
|
|
587
603
|
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
604
|
+
let channelId = normalizeSlackChannelId(stripSlackTargetPrefix(rawTarget));
|
|
605
|
+
if (/^U/i.test(channelId)) {
|
|
606
|
+
const resolved = await resolveUserToDmChannel(web, channelId, log);
|
|
607
|
+
if (!resolved) return null;
|
|
608
|
+
channelId = resolved;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
return { channelId, threadTs };
|
|
592
612
|
}
|
|
593
613
|
|
|
594
614
|
function parseSlackThreadSessionKey(sessionKey: string): SlackThreadTarget | null {
|
|
@@ -734,7 +754,38 @@ function escapeMrkdwn(value: string): string {
|
|
|
734
754
|
}
|
|
735
755
|
|
|
736
756
|
function normalizeSlackChannelId(channelId: string): string {
|
|
737
|
-
return /^[
|
|
757
|
+
return /^[cgdu]/i.test(channelId) ? channelId.toUpperCase() : channelId;
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
/** Cache of userId → DM channelId to avoid repeated conversations.open calls */
|
|
761
|
+
const dmChannelCache = new Map<string, string>();
|
|
762
|
+
|
|
763
|
+
async function resolveUserToDmChannel(
|
|
764
|
+
web: WebClient,
|
|
765
|
+
userId: string,
|
|
766
|
+
log: Logger,
|
|
767
|
+
): Promise<string | undefined> {
|
|
768
|
+
const normalized = userId.toUpperCase();
|
|
769
|
+
const cached = dmChannelCache.get(normalized);
|
|
770
|
+
if (cached) return cached;
|
|
771
|
+
|
|
772
|
+
try {
|
|
773
|
+
const result = await withSlackRetry(
|
|
774
|
+
() => web.conversations.open({ users: normalized, return_im: true }),
|
|
775
|
+
log,
|
|
776
|
+
);
|
|
777
|
+
const channelId = asNonEmptyString((result as any)?.channel?.id);
|
|
778
|
+
if (channelId) {
|
|
779
|
+
dmChannelCache.set(normalized, channelId);
|
|
780
|
+
log.info(`slack-subagent-card: resolved DM channel for ${normalized} → ${channelId}`);
|
|
781
|
+
return channelId;
|
|
782
|
+
}
|
|
783
|
+
log.warn(`slack-subagent-card: conversations.open returned no channel for ${normalized}`);
|
|
784
|
+
return undefined;
|
|
785
|
+
} catch (error) {
|
|
786
|
+
log.warn(`slack-subagent-card: failed to resolve DM channel for ${normalized}: ${stringifyError(error)}`);
|
|
787
|
+
return undefined;
|
|
788
|
+
}
|
|
738
789
|
}
|
|
739
790
|
|
|
740
791
|
function cleanupStaleRuns(shared: SharedState, log: Logger): void {
|
package/openclaw.plugin.json
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "slack-subagent-card",
|
|
3
3
|
"name": "Slack Subagent Card",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.3",
|
|
5
5
|
"description": "Posts and updates a Slack Block Kit status card for thread-bound sub-agents, nudges quiet parent threads after completion, wakes parent sessions, and writes completion markers.",
|
|
6
|
-
"entry": "index.ts",
|
|
7
6
|
"configSchema": {
|
|
8
7
|
"type": "object",
|
|
9
8
|
"properties": {
|
|
10
9
|
"nudgeDelaySec": {
|
|
11
|
-
"type": "
|
|
10
|
+
"type": "integer",
|
|
12
11
|
"minimum": 10,
|
|
13
12
|
"maximum": 300,
|
|
14
13
|
"default": 30,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@unblocklabs/slack-subagent-card",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "OpenClaw plugin: Slack Block Kit status cards for sub-agents, nudge timer, parent session wake, and completion markers.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"openclaw": {
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"author": "Unblock Labs",
|
|
21
21
|
"repository": {
|
|
22
22
|
"type": "git",
|
|
23
|
-
"url": "https://github.com/
|
|
23
|
+
"url": "https://github.com/bill492/slack-subagent-card"
|
|
24
24
|
},
|
|
25
25
|
"engines": {
|
|
26
26
|
"node": ">=22"
|