@cursor/july 0.1.6 → 0.1.8
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/dist/bin/agent-serve.js +73 -19
- package/dist/channels/slack/channel-watch.d.ts +6 -1
- package/dist/channels/slack/channel-watch.d.ts.map +1 -1
- package/dist/channels/slack/channel-watch.js +22 -1
- package/dist/channels/slack/cursor-account.d.ts +4 -0
- package/dist/channels/slack/cursor-account.d.ts.map +1 -1
- package/dist/channels/slack/cursor-account.js +12 -2
- package/dist/channels/slack/defaults.d.ts +10 -5
- package/dist/channels/slack/defaults.d.ts.map +1 -1
- package/dist/channels/slack/defaults.js +39 -17
- package/dist/channels/slack/dispatch.d.ts +18 -0
- package/dist/channels/slack/dispatch.d.ts.map +1 -1
- package/dist/channels/slack/dispatch.js +141 -38
- package/dist/channels/slack/inbound.d.ts +13 -3
- package/dist/channels/slack/inbound.d.ts.map +1 -1
- package/dist/channels/slack/inbound.js +17 -9
- package/dist/channels/slack/index.d.ts +4 -0
- package/dist/channels/slack/index.d.ts.map +1 -1
- package/dist/channels/slack/index.js +4 -0
- package/dist/channels/slack/live-delivery.d.ts.map +1 -1
- package/dist/channels/slack/live-delivery.js +1 -0
- package/dist/channels/slack/manifest.d.ts.map +1 -1
- package/dist/channels/slack/manifest.js +3 -0
- package/dist/channels/slack/nudge-carrier.d.ts +19 -0
- package/dist/channels/slack/nudge-carrier.d.ts.map +1 -0
- package/dist/channels/slack/nudge-carrier.js +166 -0
- package/dist/channels/slack/nudge-store.d.ts +61 -0
- package/dist/channels/slack/nudge-store.d.ts.map +1 -0
- package/dist/channels/slack/nudge-store.js +266 -0
- package/dist/channels/slack/slack-channel.d.ts.map +1 -1
- package/dist/channels/slack/slack-channel.js +74 -25
- package/dist/channels/slack/stop.d.ts +14 -0
- package/dist/channels/slack/stop.d.ts.map +1 -0
- package/dist/channels/slack/stop.js +22 -0
- package/dist/channels/slack/thread-context.d.ts +81 -0
- package/dist/channels/slack/thread-context.d.ts.map +1 -0
- package/dist/channels/slack/thread-context.js +470 -0
- package/dist/channels/slack/types.d.ts +27 -0
- package/dist/channels/slack/types.d.ts.map +1 -1
- package/dist/docs/404.html +2 -2
- package/dist/docs/ab.html +4 -4
- package/dist/docs/assets/{app.DMboGKUD.js → app.BP8mAVMW.js} +1 -1
- package/dist/docs/assets/chunks/@localSearchIndexroot.DevOabKJ.js +1 -0
- package/dist/docs/assets/chunks/{VPLocalSearchBox.DkEqEnJU.js → VPLocalSearchBox.1FXIZbz8.js} +1 -1
- package/dist/docs/assets/chunks/{theme.ASugdMNm.js → theme.Dm1R02v9.js} +2 -2
- package/dist/docs/assets/example-agents_index.md.CwtWu5ix.js +2 -0
- package/dist/docs/assets/{example-agents_index.md.CRqJlnIf.lean.js → example-agents_index.md.CwtWu5ix.lean.js} +1 -1
- package/dist/docs/assets/example-agents_oncall.md.BG_sUMly.js +10 -0
- package/dist/docs/assets/example-agents_oncall.md.BG_sUMly.lean.js +1 -0
- package/dist/docs/assets/{guides_slack.md.CCwqHvSV.js → guides_slack.md.FWale6Ip.js} +4 -3
- package/dist/docs/assets/{guides_slack.md.CCwqHvSV.lean.js → guides_slack.md.FWale6Ip.lean.js} +1 -1
- package/dist/docs/assets/{reference_sessions.md.DdjWabnN.js → reference_sessions.md.DLd6mvbv.js} +2 -2
- package/dist/docs/assets/{reference_sessions.md.DdjWabnN.lean.js → reference_sessions.md.DLd6mvbv.lean.js} +1 -1
- package/dist/docs/assets/{storage.md.CVnInNiN.js → storage.md.Cu3MwM2a.js} +3 -2
- package/dist/docs/assets/{storage.md.CVnInNiN.lean.js → storage.md.Cu3MwM2a.lean.js} +1 -1
- package/dist/docs/building-with-agents.html +4 -4
- package/dist/docs/concepts.html +4 -4
- package/dist/docs/deployment.html +4 -4
- package/dist/docs/evals.html +4 -4
- package/dist/docs/example-agents/approval-buddy.html +4 -4
- package/dist/docs/example-agents/benny.html +5 -5
- package/dist/docs/example-agents/bugbot.html +5 -5
- package/dist/docs/example-agents/codebase-wiki.html +4 -4
- package/dist/docs/example-agents/codeowners-review.html +4 -4
- package/dist/docs/example-agents/concierge.html +4 -4
- package/dist/docs/example-agents/fsd.html +4 -4
- package/dist/docs/example-agents/index.html +6 -6
- package/dist/docs/example-agents/knowledge-base.html +4 -4
- package/dist/docs/example-agents/oncall.html +34 -0
- package/dist/docs/example-agents/security-reviewer.html +4 -4
- package/dist/docs/example-agents/slack-agent.html +4 -4
- package/dist/docs/example-agents/weather-agent.html +4 -4
- package/dist/docs/guides/agent-to-agent.html +4 -4
- package/dist/docs/guides/cloud-runtime.html +4 -4
- package/dist/docs/guides/github.html +4 -4
- package/dist/docs/guides/human-in-the-loop.html +4 -4
- package/dist/docs/guides/slack.html +7 -6
- package/dist/docs/guides/webhooks.html +4 -4
- package/dist/docs/hashmap.json +1 -1
- package/dist/docs/hillclimbing.html +4 -4
- package/dist/docs/index.html +4 -4
- package/dist/docs/quickstart.html +4 -4
- package/dist/docs/reference/agent-config.html +4 -4
- package/dist/docs/reference/channels.html +4 -4
- package/dist/docs/reference/cli.html +4 -4
- package/dist/docs/reference/connections.html +4 -4
- package/dist/docs/reference/hooks.html +4 -4
- package/dist/docs/reference/http-api.html +4 -4
- package/dist/docs/reference/instructions.html +4 -4
- package/dist/docs/reference/playground.html +4 -4
- package/dist/docs/reference/project-layout.html +4 -4
- package/dist/docs/reference/schedules.html +4 -4
- package/dist/docs/reference/sessions.html +5 -5
- package/dist/docs/reference/skills.html +4 -4
- package/dist/docs/reference/subagents.html +4 -4
- package/dist/docs/reference/tools.html +4 -4
- package/dist/docs/scaffolding-agents.html +4 -4
- package/dist/docs/storage.html +7 -6
- package/dist/docs/troubleshooting.html +4 -4
- package/dist/evals.d.ts +2 -2
- package/dist/internal/cli-ax.d.ts +23 -10
- package/dist/internal/cli-ax.d.ts.map +1 -1
- package/dist/internal/cli-ax.js +358 -55
- package/dist/internal/cli-mcp.d.ts +64 -0
- package/dist/internal/cli-mcp.d.ts.map +1 -0
- package/dist/internal/cli-mcp.js +570 -0
- package/dist/internal/distribution.d.ts.map +1 -1
- package/dist/internal/distribution.js +2 -0
- package/dist/internal/eval-run-store.d.ts +2 -0
- package/dist/internal/eval-run-store.d.ts.map +1 -1
- package/dist/internal/eval-run-store.js +4 -4
- package/dist/internal/evals-client.d.ts +96 -0
- package/dist/internal/evals-client.d.ts.map +1 -0
- package/dist/internal/evals-client.js +262 -0
- package/dist/internal/host-kv.d.ts +17 -0
- package/dist/internal/host-kv.d.ts.map +1 -0
- package/dist/internal/host-kv.js +77 -0
- package/dist/internal/init-project.js +3 -3
- package/dist/internal/server.d.ts.map +1 -1
- package/dist/internal/server.js +19 -2
- package/dist/internal/session-engine.d.ts +42 -7
- package/dist/internal/session-engine.d.ts.map +1 -1
- package/dist/internal/session-engine.js +247 -51
- package/dist/internal/storage-coordinator.d.ts +18 -1
- package/dist/internal/storage-coordinator.d.ts.map +1 -1
- package/dist/internal/storage-coordinator.js +43 -0
- package/dist/playground/assets/index-Bpr4Th4M.js +319 -0
- package/dist/playground/index.html +1 -1
- package/dist/storage-backends/file-kv.d.ts +42 -0
- package/dist/storage-backends/file-kv.d.ts.map +1 -0
- package/dist/storage-backends/file-kv.js +190 -0
- package/dist/storage-backends/postgres-kv.d.ts +77 -0
- package/dist/storage-backends/postgres-kv.d.ts.map +1 -0
- package/dist/storage-backends/postgres-kv.js +180 -0
- package/dist/storage.d.ts +10 -1
- package/dist/storage.d.ts.map +1 -1
- package/dist/storage.js +10 -1
- package/dist/types.d.ts +79 -0
- package/dist/types.d.ts.map +1 -1
- package/docs/example-agents/index.md +14 -9
- package/docs/example-agents/oncall.md +216 -0
- package/docs/guides/slack.md +11 -2
- package/docs/reference/sessions.md +13 -4
- package/docs/storage.md +27 -0
- package/package.json +15 -1
- package/skills/framework-map/SKILL.md +6 -4
- package/src/bin/agent-serve.ts +79 -18
- package/src/channels/slack/channel-watch.ts +28 -1
- package/src/channels/slack/cursor-account.ts +20 -0
- package/src/channels/slack/defaults.ts +51 -20
- package/src/channels/slack/dispatch.ts +215 -40
- package/src/channels/slack/inbound.ts +30 -10
- package/src/channels/slack/index.ts +4 -0
- package/src/channels/slack/live-delivery.ts +1 -0
- package/src/channels/slack/manifest.ts +3 -0
- package/src/channels/slack/nudge-carrier.ts +182 -0
- package/src/channels/slack/nudge-store.ts +308 -0
- package/src/channels/slack/slack-channel.ts +106 -31
- package/src/channels/slack/stop.ts +25 -0
- package/src/channels/slack/thread-context.ts +572 -0
- package/src/channels/slack/types.ts +27 -0
- package/src/evals.ts +2 -2
- package/src/internal/cli-ax.ts +466 -75
- package/src/internal/cli-mcp.ts +708 -0
- package/src/internal/distribution.ts +2 -0
- package/src/internal/eval-run-store.ts +5 -0
- package/src/internal/evals-client.ts +431 -0
- package/src/internal/host-kv.ts +85 -0
- package/src/internal/init-project.ts +3 -3
- package/src/internal/server.ts +43 -0
- package/src/internal/session-engine.ts +351 -50
- package/src/internal/storage-coordinator.ts +41 -0
- package/src/storage-backends/file-kv.ts +221 -0
- package/src/storage-backends/postgres-kv.ts +248 -0
- package/src/storage.ts +11 -1
- package/src/types.ts +83 -0
- package/dist/docs/assets/chunks/@localSearchIndexroot.O6eP7QZr.js +0 -1
- package/dist/docs/assets/example-agents_index.md.CRqJlnIf.js +0 -2
- package/dist/playground/assets/index-mux9aj5P.js +0 -319
|
@@ -10,22 +10,31 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
10
10
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
11
11
|
});
|
|
12
12
|
};
|
|
13
|
-
import { SessionBusyError } from "../../internal/session-engine.js";
|
|
14
13
|
import { buildSlackBinding, slackContinuationKey } from "./api.js";
|
|
15
14
|
import { firstLineTitle, maybeSetThreadTitle } from "./assistant.js";
|
|
16
15
|
import { formatSlackContextBlock, parseAppMentionEvent, parseChannelMessageEvent, parseDirectMessageEvent, } from "./inbound.js";
|
|
17
16
|
import { defaultSlackLog } from "./log.js";
|
|
18
|
-
|
|
17
|
+
import { isWholeMessageStop } from "./stop.js";
|
|
18
|
+
import { loadSlackThreadContextBlock, loadSlackThreadDeltaBlock, } from "./thread-context.js";
|
|
19
|
+
const FOLLOWUP_TYPING = "Saw your follow-up…";
|
|
19
20
|
/** Blank per-session adapter state. */
|
|
20
21
|
export function emptySlackChannelState(patch = {}) {
|
|
21
|
-
return Object.assign({ channelId: null, threadTs: null, teamId: null, triggeringUserId: null, pendingToolCallMessage: null, lastReasoningTypingAtMs: null, lastReasoningTypingStatus: null, streamTs: null, streamChannelId: null, lastPostedText: null, streamingUnavailable: false, approvalMessageByCallId: {} }, patch);
|
|
22
|
+
return Object.assign({ channelId: null, threadTs: null, teamId: null, triggeringUserId: null, pendingToolCallMessage: null, lastReasoningTypingAtMs: null, lastReasoningTypingStatus: null, streamTs: null, streamChannelId: null, lastPostedText: null, lastBotMessageTs: null, streamingUnavailable: false, approvalMessageByCallId: {} }, patch);
|
|
23
|
+
}
|
|
24
|
+
function triggeringMergeMessage(message) {
|
|
25
|
+
var _a, _b;
|
|
26
|
+
return Object.assign(Object.assign(Object.assign({ ts: message.ts }, (((_a = message.author) === null || _a === void 0 ? void 0 : _a.userId) === undefined || message.author.userId === ""
|
|
27
|
+
? {}
|
|
28
|
+
: { user: message.author.userId })), (((_b = message.author) === null || _b === void 0 ? void 0 : _b.botId) === undefined
|
|
29
|
+
? {}
|
|
30
|
+
: { bot_id: message.author.botId })), { text: message.text });
|
|
22
31
|
}
|
|
23
32
|
export function dispatchInboundMessage(args) {
|
|
24
33
|
return __awaiter(this, void 0, void 0, function* () {
|
|
25
|
-
var _a, _b, _c, _d, _e, _f;
|
|
26
|
-
var
|
|
34
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
|
|
35
|
+
var _m, _o, _p, _q, _r, _s, _t, _u;
|
|
27
36
|
const { message, kind } = args;
|
|
28
|
-
const log = (
|
|
37
|
+
const log = (_m = args.log) !== null && _m !== void 0 ? _m : defaultSlackLog;
|
|
29
38
|
const binding = buildSlackBinding(Object.assign({ botToken: (_a = args.credentials) === null || _a === void 0 ? void 0 : _a.botToken, channelId: message.channelId, threadTs: message.threadTs, host: args.host, log }, (message.teamId === undefined ? {} : { teamId: message.teamId })));
|
|
30
39
|
log.info("inbound", {
|
|
31
40
|
kind,
|
|
@@ -36,9 +45,32 @@ export function dispatchInboundMessage(args) {
|
|
|
36
45
|
user: (_b = message.author) === null || _b === void 0 ? void 0 : _b.userId,
|
|
37
46
|
team: message.teamId,
|
|
38
47
|
});
|
|
48
|
+
const continuation = slackContinuationKey(message.channelId, message.threadTs);
|
|
49
|
+
// Whole-message stop: cancel active turn + clear nudges (Benny parity).
|
|
50
|
+
if (isWholeMessageStop(message.text) ||
|
|
51
|
+
isWholeMessageStop(message.markdown)) {
|
|
52
|
+
log.info("stop directive", {
|
|
53
|
+
kind,
|
|
54
|
+
channel: message.channelId,
|
|
55
|
+
thread_ts: message.threadTs,
|
|
56
|
+
continuation,
|
|
57
|
+
event_id: args.eventId,
|
|
58
|
+
});
|
|
59
|
+
yield (_c = args.host.slackNudges) === null || _c === void 0 ? void 0 : _c.clear(continuation);
|
|
60
|
+
const interrupted = args.interruptContinuation === undefined
|
|
61
|
+
? false
|
|
62
|
+
: yield args.interruptContinuation(continuation);
|
|
63
|
+
try {
|
|
64
|
+
yield binding.thread.startTyping(interrupted ? "Stopped." : "Nothing to stop.");
|
|
65
|
+
}
|
|
66
|
+
catch (_v) {
|
|
67
|
+
// best-effort status
|
|
68
|
+
}
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
39
71
|
if (message.ts === message.threadTs) {
|
|
40
72
|
yield maybeSetThreadTitle({
|
|
41
|
-
botToken: (
|
|
73
|
+
botToken: (_d = args.credentials) === null || _d === void 0 ? void 0 : _d.botToken,
|
|
42
74
|
channelId: message.channelId,
|
|
43
75
|
threadTs: message.threadTs,
|
|
44
76
|
title: firstLineTitle(message.markdown),
|
|
@@ -67,47 +99,90 @@ export function dispatchInboundMessage(args) {
|
|
|
67
99
|
});
|
|
68
100
|
return;
|
|
69
101
|
}
|
|
70
|
-
|
|
102
|
+
// Reply-once / delayed parent: claim channel:ts after the handler accepts.
|
|
103
|
+
const claimed = yield (_e = args.host.slackNudges) === null || _e === void 0 ? void 0 : _e.tryClaimAsk(message.channelId, message.ts);
|
|
104
|
+
if (claimed === false) {
|
|
105
|
+
log.info("inbound ask dedupe drop", {
|
|
106
|
+
kind,
|
|
107
|
+
channel: message.channelId,
|
|
108
|
+
message_ts: message.ts,
|
|
109
|
+
continuation,
|
|
110
|
+
event_id: args.eventId,
|
|
111
|
+
});
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
const userText = (_o = decision.message) !== null && _o !== void 0 ? _o : message.markdown;
|
|
115
|
+
const excludeThreadTimestamps = [
|
|
116
|
+
message.ts,
|
|
117
|
+
...((_p = decision.excludeThreadTimestamps) !== null && _p !== void 0 ? _p : []),
|
|
118
|
+
];
|
|
119
|
+
const hasSession = args.hasContinuationSession === undefined
|
|
120
|
+
? false
|
|
121
|
+
: yield args.hasContinuationSession(continuation);
|
|
122
|
+
const mergeMessages = [triggeringMergeMessage(message)];
|
|
123
|
+
const afterTs = hasSession && args.getContinuationLastBotMessageTs !== undefined
|
|
124
|
+
? yield args.getContinuationLastBotMessageTs(continuation)
|
|
125
|
+
: undefined;
|
|
126
|
+
// Cold start: full <slack_thread>. Warm: <slack_thread_delta> since last
|
|
127
|
+
// agent post (session watermark), else last matching bot post in-thread.
|
|
128
|
+
const threadContext = hasSession
|
|
129
|
+
? yield loadSlackThreadDeltaBlock(Object.assign(Object.assign({ botToken: (_f = args.credentials) === null || _f === void 0 ? void 0 : _f.botToken, channelId: message.channelId, threadTs: message.threadTs }, (afterTs === undefined ? {} : { afterTs })), { excludeTs: excludeThreadTimestamps, mergeMessages,
|
|
130
|
+
log }))
|
|
131
|
+
: yield loadSlackThreadContextBlock({
|
|
132
|
+
botToken: (_g = args.credentials) === null || _g === void 0 ? void 0 : _g.botToken,
|
|
133
|
+
channelId: message.channelId,
|
|
134
|
+
threadTs: message.threadTs,
|
|
135
|
+
excludeTs: excludeThreadTimestamps,
|
|
136
|
+
mergeMessages,
|
|
137
|
+
log,
|
|
138
|
+
});
|
|
139
|
+
if (threadContext !== undefined) {
|
|
140
|
+
log.info(hasSession ? "thread delta loaded" : "thread context loaded", Object.assign({ kind, channel: message.channelId, thread_ts: message.threadTs, chars: threadContext.length, event_id: args.eventId }, (afterTs === undefined ? {} : { after_ts: afterTs })));
|
|
141
|
+
}
|
|
71
142
|
const prompt = [
|
|
72
|
-
formatSlackContextBlock(Object.assign(Object.assign({ userId: (
|
|
143
|
+
formatSlackContextBlock(Object.assign(Object.assign({ userId: (_q = (_h = message.author) === null || _h === void 0 ? void 0 : _h.userId) !== null && _q !== void 0 ? _q : "", channelId: message.channelId, threadTs: message.threadTs }, (((_j = message.author) === null || _j === void 0 ? void 0 : _j.userName) === undefined
|
|
73
144
|
? {}
|
|
74
145
|
: { userName: message.author.userName })), (message.teamId === undefined ? {} : { teamId: message.teamId }))),
|
|
75
|
-
...(
|
|
146
|
+
...(threadContext === undefined ? [] : [threadContext]),
|
|
147
|
+
...((_r = decision.context) !== null && _r !== void 0 ? _r : []),
|
|
76
148
|
"",
|
|
77
149
|
userText,
|
|
78
150
|
]
|
|
79
151
|
.filter((part) => part.length > 0)
|
|
80
152
|
.join("\n");
|
|
81
|
-
const
|
|
153
|
+
const sendOptions = {
|
|
154
|
+
auth: decision.auth,
|
|
155
|
+
continuationToken: continuation,
|
|
156
|
+
admission: "coalesce",
|
|
157
|
+
coalesceSourceTs: message.ts,
|
|
158
|
+
state: emptySlackChannelState({
|
|
159
|
+
channelId: message.channelId,
|
|
160
|
+
threadTs: message.threadTs,
|
|
161
|
+
teamId: (_s = message.teamId) !== null && _s !== void 0 ? _s : null,
|
|
162
|
+
triggeringUserId: (_t = (_k = message.author) === null || _k === void 0 ? void 0 : _k.userId) !== null && _t !== void 0 ? _t : null,
|
|
163
|
+
}),
|
|
164
|
+
title: (_u = decision.title) !== null && _u !== void 0 ? _u : `slack:${message.channelId}:${message.threadTs}`,
|
|
165
|
+
workspaceDir: decision.workspaceDir,
|
|
166
|
+
workspaceFiles: decision.workspaceFiles,
|
|
167
|
+
cloud: decision.cloud,
|
|
168
|
+
sdkAgentId: decision.sdkAgentId,
|
|
169
|
+
};
|
|
82
170
|
try {
|
|
83
|
-
const session = yield args.send(prompt,
|
|
84
|
-
|
|
85
|
-
continuationToken: continuation,
|
|
86
|
-
state: emptySlackChannelState({
|
|
87
|
-
channelId: message.channelId,
|
|
88
|
-
threadTs: message.threadTs,
|
|
89
|
-
teamId: (_l = message.teamId) !== null && _l !== void 0 ? _l : null,
|
|
90
|
-
triggeringUserId: (_m = (_f = message.author) === null || _f === void 0 ? void 0 : _f.userId) !== null && _m !== void 0 ? _m : null,
|
|
91
|
-
}),
|
|
92
|
-
title: (_o = decision.title) !== null && _o !== void 0 ? _o : `slack:${message.channelId}:${message.threadTs}`,
|
|
93
|
-
workspaceDir: decision.workspaceDir,
|
|
94
|
-
workspaceFiles: decision.workspaceFiles,
|
|
95
|
-
cloud: decision.cloud,
|
|
96
|
-
sdkAgentId: decision.sdkAgentId,
|
|
97
|
-
});
|
|
98
|
-
log.info("session start", {
|
|
171
|
+
const session = yield args.send(prompt, sendOptions);
|
|
172
|
+
log.info(session.coalesced === true ? "session coalesced" : "session start", {
|
|
99
173
|
kind,
|
|
100
174
|
session: session.id,
|
|
101
175
|
is_new: session.isNew,
|
|
176
|
+
coalesced: session.coalesced === true,
|
|
102
177
|
continuation,
|
|
103
178
|
channel: message.channelId,
|
|
104
179
|
thread_ts: message.threadTs,
|
|
105
180
|
event_id: args.eventId,
|
|
106
181
|
});
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
if (
|
|
110
|
-
log.info("session busy;
|
|
182
|
+
// Coalesce admits leave the active turn running; follow-ups drain into one
|
|
183
|
+
// boundary turn when it finishes (no mid-turn tool/hook inject).
|
|
184
|
+
if (session.coalesced === true) {
|
|
185
|
+
log.info("session busy; coalescing follow-up", {
|
|
111
186
|
kind,
|
|
112
187
|
channel: message.channelId,
|
|
113
188
|
thread_ts: message.threadTs,
|
|
@@ -115,17 +190,21 @@ export function dispatchInboundMessage(args) {
|
|
|
115
190
|
event_id: args.eventId,
|
|
116
191
|
});
|
|
117
192
|
try {
|
|
118
|
-
yield binding.thread.
|
|
193
|
+
yield binding.thread.startTyping(FOLLOWUP_TYPING);
|
|
119
194
|
}
|
|
120
|
-
catch (
|
|
121
|
-
log.error(`
|
|
195
|
+
catch (typingError) {
|
|
196
|
+
log.error(`follow-up typing failed: ${typingError instanceof Error
|
|
197
|
+
? typingError.message
|
|
198
|
+
: String(typingError)}`, {
|
|
122
199
|
channel: message.channelId,
|
|
123
200
|
thread_ts: message.threadTs,
|
|
124
201
|
event_id: args.eventId,
|
|
125
202
|
});
|
|
126
203
|
}
|
|
127
|
-
return;
|
|
128
204
|
}
|
|
205
|
+
}
|
|
206
|
+
catch (error) {
|
|
207
|
+
yield (_l = args.host.slackNudges) === null || _l === void 0 ? void 0 : _l.releaseAsk(message.channelId, message.ts);
|
|
129
208
|
log.error(`${kind} delivery failed: ${error instanceof Error ? error.message : String(error)}`, {
|
|
130
209
|
channel: message.channelId,
|
|
131
210
|
thread_ts: message.threadTs,
|
|
@@ -160,7 +239,17 @@ export function dispatchInboundFromEventBody(body, deps) {
|
|
|
160
239
|
log.debug("inbound ignored (mentions disabled)", { event_id: eventId });
|
|
161
240
|
return false;
|
|
162
241
|
}
|
|
163
|
-
deps.waitUntil(dispatchInboundMessage(Object.assign({ kind: "app_mention", message: mention, handler: deps.onAppMention, send: deps.send, credentials: deps.credentials, host: deps.host, log }, (
|
|
242
|
+
deps.waitUntil(dispatchInboundMessage(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({ kind: "app_mention", message: mention, handler: deps.onAppMention, send: deps.send, credentials: deps.credentials, host: deps.host, log }, (deps.hasContinuationSession === undefined
|
|
243
|
+
? {}
|
|
244
|
+
: { hasContinuationSession: deps.hasContinuationSession })), (deps.isContinuationBusy === undefined
|
|
245
|
+
? {}
|
|
246
|
+
: { isContinuationBusy: deps.isContinuationBusy })), (deps.getContinuationLastBotMessageTs === undefined
|
|
247
|
+
? {}
|
|
248
|
+
: {
|
|
249
|
+
getContinuationLastBotMessageTs: deps.getContinuationLastBotMessageTs,
|
|
250
|
+
})), (deps.interruptContinuation === undefined
|
|
251
|
+
? {}
|
|
252
|
+
: { interruptContinuation: deps.interruptContinuation })), (eventId === undefined ? {} : { eventId }))));
|
|
164
253
|
return true;
|
|
165
254
|
}
|
|
166
255
|
const dm = parseDirectMessageEvent(body);
|
|
@@ -171,11 +260,25 @@ export function dispatchInboundFromEventBody(body, deps) {
|
|
|
171
260
|
});
|
|
172
261
|
return false;
|
|
173
262
|
}
|
|
174
|
-
deps.waitUntil(dispatchInboundMessage(Object.assign({ kind: "direct_message", message: dm, handler: deps.onDirectMessage, send: deps.send, credentials: deps.credentials, host: deps.host, log }, (
|
|
263
|
+
deps.waitUntil(dispatchInboundMessage(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({ kind: "direct_message", message: dm, handler: deps.onDirectMessage, send: deps.send, credentials: deps.credentials, host: deps.host, log }, (deps.hasContinuationSession === undefined
|
|
264
|
+
? {}
|
|
265
|
+
: { hasContinuationSession: deps.hasContinuationSession })), (deps.isContinuationBusy === undefined
|
|
266
|
+
? {}
|
|
267
|
+
: { isContinuationBusy: deps.isContinuationBusy })), (deps.getContinuationLastBotMessageTs === undefined
|
|
268
|
+
? {}
|
|
269
|
+
: {
|
|
270
|
+
getContinuationLastBotMessageTs: deps.getContinuationLastBotMessageTs,
|
|
271
|
+
})), (deps.interruptContinuation === undefined
|
|
272
|
+
? {}
|
|
273
|
+
: { interruptContinuation: deps.interruptContinuation })), (eventId === undefined ? {} : { eventId }))));
|
|
175
274
|
return true;
|
|
176
275
|
}
|
|
177
276
|
if (deps.channelWatch !== undefined) {
|
|
178
|
-
|
|
277
|
+
// Parse keeps bot-authored posts; the watcher enforces the per-channel
|
|
278
|
+
// policy (includeBotPosts, own-post loop safety) with skip logging.
|
|
279
|
+
const channelEvent = parseChannelMessageEvent(body, {
|
|
280
|
+
includeBotAuthored: true,
|
|
281
|
+
});
|
|
179
282
|
if (channelEvent !== null) {
|
|
180
283
|
return deps.channelWatch.handle(channelEvent, eventId);
|
|
181
284
|
}
|
|
@@ -23,14 +23,24 @@ export type SlackChannelMessageEvent = {
|
|
|
23
23
|
channelId: string;
|
|
24
24
|
ts: string;
|
|
25
25
|
};
|
|
26
|
+
export interface ParseChannelMessageOptions {
|
|
27
|
+
/**
|
|
28
|
+
* Keep bot-authored posts (including `subtype: "bot_message"` webhook
|
|
29
|
+
* posts) instead of dropping them at parse time. The channel watch
|
|
30
|
+
* enforces its own per-channel policy ({@link SlackChannelPostsOptions.includeBotPosts})
|
|
31
|
+
* and always drops the watching bot's own posts.
|
|
32
|
+
*/
|
|
33
|
+
includeBotAuthored?: boolean;
|
|
34
|
+
}
|
|
26
35
|
/**
|
|
27
36
|
* Parse channel / private-channel message events for the opt-in channel
|
|
28
37
|
* watch. Accepts `channel_type === "channel" | "group"` (or `C…`/`G…`
|
|
29
38
|
* channel ids when the type is absent). Bot-authored messages are dropped
|
|
30
|
-
* (loop safety)
|
|
31
|
-
* update or cancel a
|
|
39
|
+
* (loop safety) unless `options.includeBotAuthored` is set; edits and
|
|
40
|
+
* deletions are surfaced so a debounce window can update or cancel a
|
|
41
|
+
* pending dispatch.
|
|
32
42
|
*/
|
|
33
|
-
export declare function parseChannelMessageEvent(body: unknown): SlackChannelMessageEvent | null;
|
|
43
|
+
export declare function parseChannelMessageEvent(body: unknown, options?: ParseChannelMessageOptions): SlackChannelMessageEvent | null;
|
|
34
44
|
export declare function formatSlackContextBlock(args: {
|
|
35
45
|
userId: string;
|
|
36
46
|
userName?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"inbound.d.ts","sourceRoot":"","sources":["../../../src/channels/slack/inbound.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAe,YAAY,EAAE,MAAM,YAAY,CAAC;AAe5D,oEAAoE;AACpE,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CASxD;
|
|
1
|
+
{"version":3,"file":"inbound.d.ts","sourceRoot":"","sources":["../../../src/channels/slack/inbound.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAe,YAAY,EAAE,MAAM,YAAY,CAAC;AAe5D,oEAAoE;AACpE,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CASxD;AAgDD,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,OAAO,GAAG,YAAY,GAAG,IAAI,CAevE;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,OAAO,GAAG,YAAY,GAAG,IAAI,CA8B1E;AAED,4EAA4E;AAC5E,MAAM,MAAM,wBAAwB,GAChC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,YAAY,CAAA;CAAE,GACvC;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,OAAO,EAAE,YAAY,CAAA;CAAE,GACzC;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC;AAEvD,MAAM,WAAW,0BAA0B;IACzC;;;;;OAKG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAED;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,OAAO,EACb,OAAO,CAAC,EAAE,0BAA0B,GACnC,wBAAwB,GAAG,IAAI,CA4DjC;AAED,wBAAgB,uBAAuB,CAAC,IAAI,EAAE;IAC5C,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,GAAG,MAAM,CAYT"}
|
|
@@ -21,12 +21,15 @@ export function slackTextToMarkdown(text) {
|
|
|
21
21
|
}
|
|
22
22
|
function parseAuthor(event) {
|
|
23
23
|
const userId = asString(event.user);
|
|
24
|
-
|
|
24
|
+
const botId = asString(event.bot_id);
|
|
25
|
+
const isBot = botId !== undefined && botId.length > 0;
|
|
26
|
+
// Webhook-style bot posts can lack `user`; keep a bot author so the
|
|
27
|
+
// channel watch can attribute (and self-filter) them by `bot_id`.
|
|
28
|
+
if ((userId === undefined || userId === "") && !isBot) {
|
|
25
29
|
return undefined;
|
|
26
30
|
}
|
|
27
|
-
const botId = asString(event.bot_id);
|
|
28
31
|
const userName = asString(event.username);
|
|
29
|
-
return Object.assign(Object.assign({ userId }, (userName === undefined ? {} : { userName })), { isBot
|
|
32
|
+
return Object.assign(Object.assign(Object.assign({ userId: userId !== null && userId !== void 0 ? userId : "" }, (userName === undefined ? {} : { userName })), { isBot }), (isBot ? { botId } : {}));
|
|
30
33
|
}
|
|
31
34
|
function buildSlackMessage(event, teamId) {
|
|
32
35
|
var _a, _b;
|
|
@@ -99,10 +102,11 @@ export function parseDirectMessageEvent(body) {
|
|
|
99
102
|
* Parse channel / private-channel message events for the opt-in channel
|
|
100
103
|
* watch. Accepts `channel_type === "channel" | "group"` (or `C…`/`G…`
|
|
101
104
|
* channel ids when the type is absent). Bot-authored messages are dropped
|
|
102
|
-
* (loop safety)
|
|
103
|
-
* update or cancel a
|
|
105
|
+
* (loop safety) unless `options.includeBotAuthored` is set; edits and
|
|
106
|
+
* deletions are surfaced so a debounce window can update or cancel a
|
|
107
|
+
* pending dispatch.
|
|
104
108
|
*/
|
|
105
|
-
export function parseChannelMessageEvent(body) {
|
|
109
|
+
export function parseChannelMessageEvent(body, options) {
|
|
106
110
|
var _a, _b, _c;
|
|
107
111
|
const root = asRecord(body);
|
|
108
112
|
if (root === null || root.type !== "event_callback") {
|
|
@@ -123,6 +127,7 @@ export function parseChannelMessageEvent(body) {
|
|
|
123
127
|
}
|
|
124
128
|
const teamId = asString(root.team_id);
|
|
125
129
|
const subtype = (_b = asString(event.subtype)) !== null && _b !== void 0 ? _b : "";
|
|
130
|
+
const includeBots = (options === null || options === void 0 ? void 0 : options.includeBotAuthored) === true;
|
|
126
131
|
if (subtype === "message_deleted") {
|
|
127
132
|
const deletedTs = (_c = asString(event.deleted_ts)) !== null && _c !== void 0 ? _c : "";
|
|
128
133
|
return deletedTs === ""
|
|
@@ -135,17 +140,20 @@ export function parseChannelMessageEvent(body) {
|
|
|
135
140
|
return null;
|
|
136
141
|
}
|
|
137
142
|
const botId = asString(nested.bot_id);
|
|
138
|
-
if (botId !== undefined && botId.length > 0) {
|
|
143
|
+
if (!includeBots && botId !== undefined && botId.length > 0) {
|
|
139
144
|
return null;
|
|
140
145
|
}
|
|
141
146
|
const message = buildSlackMessage(Object.assign(Object.assign({}, nested), { channel: channelId }), teamId);
|
|
142
147
|
return message === null ? null : { kind: "edited", message };
|
|
143
148
|
}
|
|
144
|
-
|
|
149
|
+
const allowedSubtypes = includeBots
|
|
150
|
+
? ["", "file_share", "bot_message"]
|
|
151
|
+
: ["", "file_share"];
|
|
152
|
+
if (!allowedSubtypes.includes(subtype)) {
|
|
145
153
|
return null;
|
|
146
154
|
}
|
|
147
155
|
const botId = asString(event.bot_id);
|
|
148
|
-
if (botId !== undefined && botId.length > 0) {
|
|
156
|
+
if (!includeBots && botId !== undefined && botId.length > 0) {
|
|
149
157
|
return null;
|
|
150
158
|
}
|
|
151
159
|
const message = buildSlackMessage(event, teamId);
|
|
@@ -35,11 +35,15 @@ export * from "./interactive.js";
|
|
|
35
35
|
export * from "./links.js";
|
|
36
36
|
export * from "./log.js";
|
|
37
37
|
export * from "./manifest.js";
|
|
38
|
+
export * from "./nudge-carrier.js";
|
|
39
|
+
export * from "./nudge-store.js";
|
|
38
40
|
export * from "./pr-ref.js";
|
|
39
41
|
export * from "./setup.js";
|
|
40
42
|
export * from "./slack-channel.js";
|
|
41
43
|
export * from "./socket-mode.js";
|
|
44
|
+
export * from "./stop.js";
|
|
42
45
|
export * from "./stream.js";
|
|
46
|
+
export * from "./thread-context.js";
|
|
43
47
|
export * from "./tool-approval-events.js";
|
|
44
48
|
export * from "./types.js";
|
|
45
49
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/channels/slack/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,cAAc,UAAU,CAAC;AACzB,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,WAAW,CAAC;AAC1B,cAAc,oBAAoB,CAAC;AACnC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,kBAAkB,CAAC;AACjC,cAAc,qBAAqB,CAAC;AACpC,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,sBAAsB,CAAC;AACrC,cAAc,cAAc,CAAC;AAC7B,cAAc,WAAW,CAAC;AAC1B,cAAc,kBAAkB,CAAC;AACjC,cAAc,YAAY,CAAC;AAC3B,cAAc,UAAU,CAAC;AACzB,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC;AACjC,cAAc,aAAa,CAAC;AAC5B,cAAc,2BAA2B,CAAC;AAC1C,cAAc,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/channels/slack/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,cAAc,UAAU,CAAC;AACzB,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,WAAW,CAAC;AAC1B,cAAc,oBAAoB,CAAC;AACnC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,kBAAkB,CAAC;AACjC,cAAc,qBAAqB,CAAC;AACpC,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,sBAAsB,CAAC;AACrC,cAAc,cAAc,CAAC;AAC7B,cAAc,WAAW,CAAC;AAC1B,cAAc,kBAAkB,CAAC;AACjC,cAAc,YAAY,CAAC;AAC3B,cAAc,UAAU,CAAC;AACzB,cAAc,eAAe,CAAC;AAC9B,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC;AACjC,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC;AACjC,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC;AAC5B,cAAc,qBAAqB,CAAC;AACpC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,YAAY,CAAC"}
|
|
@@ -35,10 +35,14 @@ export * from "./interactive.js";
|
|
|
35
35
|
export * from "./links.js";
|
|
36
36
|
export * from "./log.js";
|
|
37
37
|
export * from "./manifest.js";
|
|
38
|
+
export * from "./nudge-carrier.js";
|
|
39
|
+
export * from "./nudge-store.js";
|
|
38
40
|
export * from "./pr-ref.js";
|
|
39
41
|
export * from "./setup.js";
|
|
40
42
|
export * from "./slack-channel.js";
|
|
41
43
|
export * from "./socket-mode.js";
|
|
44
|
+
export * from "./stop.js";
|
|
42
45
|
export * from "./stream.js";
|
|
46
|
+
export * from "./thread-context.js";
|
|
43
47
|
export * from "./tool-approval-events.js";
|
|
44
48
|
export * from "./types.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"live-delivery.d.ts","sourceRoot":"","sources":["../../../src/channels/slack/live-delivery.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAE1D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAEzC,OAAO,KAAK,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAEnE,KAAK,YAAY,GAAG,mBAAmB,CAAC,iBAAiB,CAAC,CAAC;AAE3D,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,aAAa,GAAG,SAAS,CAAC;IACpC,MAAM,EAAE,MAAM,QAAQ,CAAC;CACxB;AAED;;;;GAIG;AACH,MAAM,WAAW,oBAAoB;IACnC,8EAA8E;IAC9E,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAC;IAClC,4CAA4C;IAC5C,KAAK,CAAC,IAAI,EAAE;QACV,OAAO,EAAE,YAAY,CAAC;QACtB,SAAS,EAAE,MAAM,CAAC;QAClB,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,MAAM,CAAC;KACnB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClB,uEAAuE;IACvE,MAAM,CAAC,IAAI,EAAE;QACX,OAAO,EAAE,YAAY,CAAC;QACtB,SAAS,EAAE,MAAM,CAAC;QAClB,QAAQ,EAAE,MAAM,CAAC;QACjB,IAAI,EAAE,MAAM,CAAC;QACb,SAAS,EAAE,MAAM,CAAC;KACnB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClB;;;OAGG;IACH,QAAQ,CAAC,IAAI,EAAE;QACb,OAAO,EAAE,YAAY,CAAC;QACtB,SAAS,EAAE,MAAM,CAAC;QAClB,QAAQ,EAAE,MAAM,CAAC;QACjB,IAAI,EAAE,MAAM,CAAC;QACb,SAAS,EAAE,MAAM,CAAC;KACnB,GAAG,OAAO,CAAC;QAAE,SAAS,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IACpC,mEAAmE;IACnE,aAAa,CAAC,IAAI,EAAE;QAClB,OAAO,EAAE,YAAY,CAAC;QACtB,SAAS,EAAE,MAAM,CAAC;QAClB,QAAQ,EAAE,MAAM,CAAC;KAClB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClB,wEAAwE;IACxE,aAAa,CAAC,IAAI,EAAE;QAAE,OAAO,EAAE,YAAY,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9D,uFAAuF;IACvF,IAAI,CAAC,IAAI,EAAE;QACT,OAAO,EAAE,YAAY,CAAC;QACtB,SAAS,EAAE,MAAM,CAAC;QAClB,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,MAAM,CAAC;QAClB,SAAS,EAAE,MAAM,CAAC;KACnB,GAAG,OAAO,CAAC;QAAE,SAAS,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;CACrC;AAED,wBAAgB,4BAA4B,CAC1C,IAAI,EAAE,gBAAgB,GACrB,oBAAoB,
|
|
1
|
+
{"version":3,"file":"live-delivery.d.ts","sourceRoot":"","sources":["../../../src/channels/slack/live-delivery.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAE1D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAEzC,OAAO,KAAK,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAEnE,KAAK,YAAY,GAAG,mBAAmB,CAAC,iBAAiB,CAAC,CAAC;AAE3D,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,aAAa,GAAG,SAAS,CAAC;IACpC,MAAM,EAAE,MAAM,QAAQ,CAAC;CACxB;AAED;;;;GAIG;AACH,MAAM,WAAW,oBAAoB;IACnC,8EAA8E;IAC9E,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAC;IAClC,4CAA4C;IAC5C,KAAK,CAAC,IAAI,EAAE;QACV,OAAO,EAAE,YAAY,CAAC;QACtB,SAAS,EAAE,MAAM,CAAC;QAClB,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,MAAM,CAAC;KACnB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClB,uEAAuE;IACvE,MAAM,CAAC,IAAI,EAAE;QACX,OAAO,EAAE,YAAY,CAAC;QACtB,SAAS,EAAE,MAAM,CAAC;QAClB,QAAQ,EAAE,MAAM,CAAC;QACjB,IAAI,EAAE,MAAM,CAAC;QACb,SAAS,EAAE,MAAM,CAAC;KACnB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClB;;;OAGG;IACH,QAAQ,CAAC,IAAI,EAAE;QACb,OAAO,EAAE,YAAY,CAAC;QACtB,SAAS,EAAE,MAAM,CAAC;QAClB,QAAQ,EAAE,MAAM,CAAC;QACjB,IAAI,EAAE,MAAM,CAAC;QACb,SAAS,EAAE,MAAM,CAAC;KACnB,GAAG,OAAO,CAAC;QAAE,SAAS,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IACpC,mEAAmE;IACnE,aAAa,CAAC,IAAI,EAAE;QAClB,OAAO,EAAE,YAAY,CAAC;QACtB,SAAS,EAAE,MAAM,CAAC;QAClB,QAAQ,EAAE,MAAM,CAAC;KAClB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClB,wEAAwE;IACxE,aAAa,CAAC,IAAI,EAAE;QAAE,OAAO,EAAE,YAAY,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9D,uFAAuF;IACvF,IAAI,CAAC,IAAI,EAAE;QACT,OAAO,EAAE,YAAY,CAAC;QACtB,SAAS,EAAE,MAAM,CAAC;QAClB,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,MAAM,CAAC;QAClB,SAAS,EAAE,MAAM,CAAC;KACnB,GAAG,OAAO,CAAC;QAAE,SAAS,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;CACrC;AAED,wBAAgB,4BAA4B,CAC1C,IAAI,EAAE,gBAAgB,GACrB,oBAAoB,CA8HtB"}
|
|
@@ -92,6 +92,7 @@ export function createStreamDeliveryStrategy(deps) {
|
|
|
92
92
|
if (stopped.ok) {
|
|
93
93
|
channel.state.streamTs = null;
|
|
94
94
|
channel.state.lastPostedText = text;
|
|
95
|
+
channel.state.lastBotMessageTs = streamTs;
|
|
95
96
|
log.info("reply delivered via stream", {
|
|
96
97
|
channel: channelId,
|
|
97
98
|
stream_ts: streamTs,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../../../src/channels/slack/manifest.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAEvD,MAAM,WAAW,gBAAgB;IAC/B,mBAAmB,EAAE;QACnB,IAAI,EAAE,MAAM,CAAC;QACb,WAAW,CAAC,EAAE,MAAM,CAAC;KACtB,CAAC;IACF,QAAQ,EAAE;QACR,QAAQ,EAAE;YACR,YAAY,EAAE,MAAM,CAAC;YACrB,aAAa,EAAE,OAAO,CAAC;SACxB,CAAC;QACF,cAAc,CAAC,EAAE;YACf,qBAAqB,EAAE,MAAM,CAAC;YAC9B,iBAAiB,EAAE,KAAK,CAAC;gBAAE,KAAK,EAAE,MAAM,CAAC;gBAAC,OAAO,EAAE,MAAM,CAAA;aAAE,CAAC,CAAC;SAC9D,CAAC;QACF,QAAQ,CAAC,EAAE;YACT,gBAAgB,EAAE,OAAO,CAAC;YAC1B,oBAAoB,EAAE,OAAO,CAAC;YAC9B,8BAA8B,EAAE,OAAO,CAAC;SACzC,CAAC;KACH,CAAC;IACF,YAAY,EAAE;QACZ,MAAM,EAAE;YACN,GAAG,EAAE,MAAM,EAAE,CAAC;SACf,CAAC;KACH,CAAC;IACF,QAAQ,EAAE;QACR,mBAAmB,EAAE;YACnB,UAAU,EAAE,MAAM,EAAE,CAAC;SACtB,CAAC;QACF,aAAa,EAAE;YACb,UAAU,EAAE,OAAO,CAAC;SACrB,CAAC;QACF,kBAAkB,EAAE,OAAO,CAAC;QAC5B,mBAAmB,EAAE,OAAO,CAAC;QAC7B,sBAAsB,EAAE,OAAO,CAAC;QAChC,cAAc,EAAE,OAAO,CAAC;KACzB,CAAC;CACH;
|
|
1
|
+
{"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../../../src/channels/slack/manifest.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAEvD,MAAM,WAAW,gBAAgB;IAC/B,mBAAmB,EAAE;QACnB,IAAI,EAAE,MAAM,CAAC;QACb,WAAW,CAAC,EAAE,MAAM,CAAC;KACtB,CAAC;IACF,QAAQ,EAAE;QACR,QAAQ,EAAE;YACR,YAAY,EAAE,MAAM,CAAC;YACrB,aAAa,EAAE,OAAO,CAAC;SACxB,CAAC;QACF,cAAc,CAAC,EAAE;YACf,qBAAqB,EAAE,MAAM,CAAC;YAC9B,iBAAiB,EAAE,KAAK,CAAC;gBAAE,KAAK,EAAE,MAAM,CAAC;gBAAC,OAAO,EAAE,MAAM,CAAA;aAAE,CAAC,CAAC;SAC9D,CAAC;QACF,QAAQ,CAAC,EAAE;YACT,gBAAgB,EAAE,OAAO,CAAC;YAC1B,oBAAoB,EAAE,OAAO,CAAC;YAC9B,8BAA8B,EAAE,OAAO,CAAC;SACzC,CAAC;KACH,CAAC;IACF,YAAY,EAAE;QACZ,MAAM,EAAE;YACN,GAAG,EAAE,MAAM,EAAE,CAAC;SACf,CAAC;KACH,CAAC;IACF,QAAQ,EAAE;QACR,mBAAmB,EAAE;YACnB,UAAU,EAAE,MAAM,EAAE,CAAC;SACtB,CAAC;QACF,aAAa,EAAE;YACb,UAAU,EAAE,OAAO,CAAC;SACrB,CAAC;QACF,kBAAkB,EAAE,OAAO,CAAC;QAC5B,mBAAmB,EAAE,OAAO,CAAC;QAC7B,sBAAsB,EAAE,OAAO,CAAC;QAChC,cAAc,EAAE,OAAO,CAAC;KACzB,CAAC;CACH;AA4CD;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,oBAAoB,GAC5B,gBAAgB,CA0ElB;AAED,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,IAAI,CAAC,oBAAoB,EAAE,KAAK,CAAC,GACzC;IACD,GAAG,EAAE,gBAAgB,CAAC;IACtB,IAAI,EAAE,gBAAgB,CAAC;CACxB,CAKA"}
|
|
@@ -14,6 +14,9 @@ const BOT_SCOPES = [
|
|
|
14
14
|
"im:read",
|
|
15
15
|
"im:write",
|
|
16
16
|
"mpim:history",
|
|
17
|
+
// Emoji acknowledgements (e.g. an agent reacting 👀 when it locks onto
|
|
18
|
+
// a watched-channel post before investigating).
|
|
19
|
+
"reactions:write",
|
|
17
20
|
"users:read",
|
|
18
21
|
];
|
|
19
22
|
const BOT_EVENTS = ["app_mention", "message.im"];
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Harness-side nudge carrier: a small Cursor hooks.json + script that drains
|
|
3
|
+
* the durable Slack nudge store at tool boundaries (MCP / builtin tools).
|
|
4
|
+
*/
|
|
5
|
+
export declare const SLACK_NUDGE_ROOT_REL = ".agent-serve/slack-nudge-root";
|
|
6
|
+
export declare const SLACK_NUDGE_CONTINUATION_REL = ".agent-serve/slack-nudge-continuation";
|
|
7
|
+
export declare const SLACK_NUDGE_CARRIER_REL = ".agent-serve/slack-nudge-carrier.mjs";
|
|
8
|
+
/** Carrier script body — reads nudge-root + continuation, drains pending. */
|
|
9
|
+
export declare const SLACK_NUDGE_CARRIER_SCRIPT: string;
|
|
10
|
+
/**
|
|
11
|
+
* Write carrier script + pointer files and ensure pre/postToolUse hooks invoke it.
|
|
12
|
+
* Safe to call every turn (idempotent overwrite of carrier + pointers).
|
|
13
|
+
*/
|
|
14
|
+
export declare function materializeSlackNudgeCarrier(args: {
|
|
15
|
+
harnessCwd: string;
|
|
16
|
+
nudgeRootDir: string;
|
|
17
|
+
continuationKey: string;
|
|
18
|
+
}): Promise<void>;
|
|
19
|
+
//# sourceMappingURL=nudge-carrier.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nudge-carrier.d.ts","sourceRoot":"","sources":["../../../src/channels/slack/nudge-carrier.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,eAAO,MAAM,oBAAoB,kCAAkC,CAAC;AACpE,eAAO,MAAM,4BAA4B,0CACA,CAAC;AAC1C,eAAO,MAAM,uBAAuB,yCAAyC,CAAC;AAE9E,6EAA6E;AAC7E,eAAO,MAAM,0BAA0B,EAAE,MA6GxC,CAAC;AAOF;;;GAGG;AACH,wBAAsB,4BAA4B,CAAC,IAAI,EAAE;IACvD,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,MAAM,CAAC;CACzB,GAAG,OAAO,CAAC,IAAI,CAAC,CA2ChB"}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Harness-side nudge carrier: a small Cursor hooks.json + script that drains
|
|
3
|
+
* the durable Slack nudge store at tool boundaries (MCP / builtin tools).
|
|
4
|
+
*/
|
|
5
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
6
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
7
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
8
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
9
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
10
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
11
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
12
|
+
});
|
|
13
|
+
};
|
|
14
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
15
|
+
import { join } from "node:path";
|
|
16
|
+
export const SLACK_NUDGE_ROOT_REL = ".agent-serve/slack-nudge-root";
|
|
17
|
+
export const SLACK_NUDGE_CONTINUATION_REL = ".agent-serve/slack-nudge-continuation";
|
|
18
|
+
export const SLACK_NUDGE_CARRIER_REL = ".agent-serve/slack-nudge-carrier.mjs";
|
|
19
|
+
/** Carrier script body — reads nudge-root + continuation, drains pending. */
|
|
20
|
+
export const SLACK_NUDGE_CARRIER_SCRIPT = `#!/usr/bin/env node
|
|
21
|
+
import { mkdir, readdir, readFile, rename, rm, writeFile } from "node:fs/promises";
|
|
22
|
+
import { dirname, join } from "node:path";
|
|
23
|
+
import { fileURLToPath } from "node:url";
|
|
24
|
+
|
|
25
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
26
|
+
const workspace = dirname(here);
|
|
27
|
+
|
|
28
|
+
function safeKey(value) {
|
|
29
|
+
const trimmed = String(value ?? "").trim();
|
|
30
|
+
if (trimmed === "" || trimmed === "." || trimmed === "..") return "_empty";
|
|
31
|
+
const sanitized = trimmed
|
|
32
|
+
.replace(/[^a-zA-Z0-9._-]+/g, "_")
|
|
33
|
+
.replace(/^\\.+/, "")
|
|
34
|
+
.replace(/\\.+/g, "_")
|
|
35
|
+
.slice(0, 180);
|
|
36
|
+
if (sanitized === "" || sanitized === "." || sanitized === "..") return "_empty";
|
|
37
|
+
return sanitized;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async function readText(rel) {
|
|
41
|
+
try {
|
|
42
|
+
return (await readFile(join(workspace, rel), "utf8")).trim();
|
|
43
|
+
} catch {
|
|
44
|
+
return "";
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async function main() {
|
|
49
|
+
const root = await readText(${JSON.stringify(SLACK_NUDGE_ROOT_REL)});
|
|
50
|
+
const continuation = await readText(
|
|
51
|
+
${JSON.stringify(SLACK_NUDGE_CONTINUATION_REL)}
|
|
52
|
+
);
|
|
53
|
+
if (root === "" || continuation === "") {
|
|
54
|
+
process.stdout.write("{}\\n");
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
const pendingDir = join(root, safeKey(continuation), "pending");
|
|
58
|
+
const deliveredDir = join(root, safeKey(continuation), "delivered");
|
|
59
|
+
let names = [];
|
|
60
|
+
try {
|
|
61
|
+
names = await readdir(pendingDir);
|
|
62
|
+
} catch {
|
|
63
|
+
process.stdout.write("{}\\n");
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
const nudges = [];
|
|
67
|
+
await mkdir(deliveredDir, { recursive: true });
|
|
68
|
+
for (const name of names) {
|
|
69
|
+
if (!name.endsWith(".json")) continue;
|
|
70
|
+
const pendingPath = join(pendingDir, name);
|
|
71
|
+
let record;
|
|
72
|
+
try {
|
|
73
|
+
record = JSON.parse(await readFile(pendingPath, "utf8"));
|
|
74
|
+
} catch {
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
const sourceTs = typeof record.sourceTs === "string" ? record.sourceTs : "";
|
|
78
|
+
if (sourceTs === "") continue;
|
|
79
|
+
const dest = join(deliveredDir, safeKey(sourceTs));
|
|
80
|
+
try {
|
|
81
|
+
await readFile(dest, "utf8");
|
|
82
|
+
await rm(pendingPath, { force: true });
|
|
83
|
+
continue;
|
|
84
|
+
} catch {
|
|
85
|
+
// not delivered yet
|
|
86
|
+
}
|
|
87
|
+
const tmp = dest + "." + process.pid + ".tmp";
|
|
88
|
+
try {
|
|
89
|
+
await writeFile(tmp, JSON.stringify(record) + "\\n", "utf8");
|
|
90
|
+
await rename(tmp, dest);
|
|
91
|
+
await rm(pendingPath, { force: true });
|
|
92
|
+
nudges.push(record);
|
|
93
|
+
} catch {
|
|
94
|
+
await rm(tmp, { force: true }).catch(() => {});
|
|
95
|
+
await rm(pendingPath, { force: true }).catch(() => {});
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
if (nudges.length === 0) {
|
|
99
|
+
process.stdout.write("{}\\n");
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
const lines = [
|
|
103
|
+
"<slack_followup>",
|
|
104
|
+
"New Slack follow-up(s) arrived while you were working. Incorporate them before your final reply. If an ask was already answered in-thread, ack briefly instead of re-answering.",
|
|
105
|
+
"",
|
|
106
|
+
];
|
|
107
|
+
for (const nudge of nudges) {
|
|
108
|
+
const who =
|
|
109
|
+
typeof nudge.author === "string" && nudge.author !== ""
|
|
110
|
+
? nudge.author
|
|
111
|
+
: "someone";
|
|
112
|
+
lines.push("- " + who + " @ " + nudge.sourceTs + ":");
|
|
113
|
+
lines.push(
|
|
114
|
+
typeof nudge.text === "string" && nudge.text.trim() !== ""
|
|
115
|
+
? nudge.text.trim()
|
|
116
|
+
: "(empty)"
|
|
117
|
+
);
|
|
118
|
+
lines.push("");
|
|
119
|
+
}
|
|
120
|
+
lines.push("</slack_followup>");
|
|
121
|
+
process.stdout.write(
|
|
122
|
+
JSON.stringify({ additional_context: lines.join("\\n").trimEnd() }) + "\\n"
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
main().catch(() => {
|
|
127
|
+
process.stdout.write("{}\\n");
|
|
128
|
+
});
|
|
129
|
+
`;
|
|
130
|
+
/**
|
|
131
|
+
* Write carrier script + pointer files and ensure pre/postToolUse hooks invoke it.
|
|
132
|
+
* Safe to call every turn (idempotent overwrite of carrier + pointers).
|
|
133
|
+
*/
|
|
134
|
+
export function materializeSlackNudgeCarrier(args) {
|
|
135
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
136
|
+
var _a, _b, _c;
|
|
137
|
+
if (args.continuationKey.trim() === "") {
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
yield mkdir(join(args.harnessCwd, ".agent-serve"), { recursive: true });
|
|
141
|
+
yield mkdir(join(args.harnessCwd, ".cursor"), { recursive: true });
|
|
142
|
+
yield writeFile(join(args.harnessCwd, SLACK_NUDGE_ROOT_REL), `${args.nudgeRootDir}\n`, "utf8");
|
|
143
|
+
yield writeFile(join(args.harnessCwd, SLACK_NUDGE_CONTINUATION_REL), `${args.continuationKey}\n`, "utf8");
|
|
144
|
+
yield writeFile(join(args.harnessCwd, SLACK_NUDGE_CARRIER_REL), SLACK_NUDGE_CARRIER_SCRIPT, "utf8");
|
|
145
|
+
const hooksPath = join(args.harnessCwd, ".cursor", "hooks.json");
|
|
146
|
+
let existing = { version: 1, hooks: {} };
|
|
147
|
+
try {
|
|
148
|
+
existing = JSON.parse(yield readFile(hooksPath, "utf8"));
|
|
149
|
+
}
|
|
150
|
+
catch (_d) {
|
|
151
|
+
// fresh
|
|
152
|
+
}
|
|
153
|
+
const hooks = Object.assign({}, ((_a = existing.hooks) !== null && _a !== void 0 ? _a : {}));
|
|
154
|
+
const command = `node ${SLACK_NUDGE_CARRIER_REL}`;
|
|
155
|
+
for (const step of ["preToolUse", "postToolUse"]) {
|
|
156
|
+
const list = [...((_b = hooks[step]) !== null && _b !== void 0 ? _b : [])].filter((entry) => !entry.command.includes("slack-nudge-carrier"));
|
|
157
|
+
list.push({ command, failClosed: false });
|
|
158
|
+
hooks[step] = list;
|
|
159
|
+
}
|
|
160
|
+
const next = {
|
|
161
|
+
version: (_c = existing.version) !== null && _c !== void 0 ? _c : 1,
|
|
162
|
+
hooks,
|
|
163
|
+
};
|
|
164
|
+
yield writeFile(hooksPath, `${JSON.stringify(next, null, 2)}\n`, "utf8");
|
|
165
|
+
});
|
|
166
|
+
}
|