@open-everywhere/provider-slack 0.8.0 → 0.8.1
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/agent/app-manifest.ts +3 -1
- package/agent/channels/slack-agent-session.ts +91 -2
- package/agent/channels/slack-events.ts +58 -4
- package/agent/channels/slack-identity.ts +34 -1
- package/agent/docs-generated.ts +19 -2
- package/dist/agent/app-manifest.d.ts.map +1 -1
- package/dist/agent/app-manifest.js +3 -1
- package/dist/agent/app-manifest.js.map +1 -1
- package/dist/agent/channels/slack-agent-session.d.ts +4 -0
- package/dist/agent/channels/slack-agent-session.d.ts.map +1 -1
- package/dist/agent/channels/slack-agent-session.js +87 -3
- package/dist/agent/channels/slack-agent-session.js.map +1 -1
- package/dist/agent/channels/slack-events.d.ts +7 -3
- package/dist/agent/channels/slack-events.d.ts.map +1 -1
- package/dist/agent/channels/slack-events.js +56 -5
- package/dist/agent/channels/slack-events.js.map +1 -1
- package/dist/agent/channels/slack-identity.d.ts +26 -0
- package/dist/agent/channels/slack-identity.d.ts.map +1 -1
- package/dist/agent/channels/slack-identity.js +33 -1
- package/dist/agent/channels/slack-identity.js.map +1 -1
- package/dist/agent/docs-generated.d.ts.map +1 -1
- package/dist/agent/docs-generated.js +19 -2
- package/dist/agent/docs-generated.js.map +1 -1
- package/package.json +2 -2
package/agent/app-manifest.ts
CHANGED
|
@@ -255,7 +255,9 @@ export function slackAppManifestTemplate(opts: SlackAppManifestOptions = {}): st
|
|
|
255
255
|
"lists:read",
|
|
256
256
|
"lists:write",
|
|
257
257
|
// The side panel (OPE-2457): Slack requires it for `app_context_changed`, the event whose
|
|
258
|
-
// subscription makes Slack attach the viewed room to each side-panel `message.im`.
|
|
258
|
+
// subscription makes Slack attach the viewed room to each side-panel `message.im`. It also
|
|
259
|
+
// covers `assistant.threads.setStatus`, the status line a turn shows while it works: the
|
|
260
|
+
// mention door's, and the DM seat's (OPE-2629, channels/slack-agent-session.ts).
|
|
259
261
|
"assistant:write",
|
|
260
262
|
// Slack requires this bot scope on an app whose people connect to Slack's MCP server
|
|
261
263
|
// (OPE-2421). Declared only when the app declares "search Slack as you".
|
|
@@ -21,6 +21,8 @@
|
|
|
21
21
|
// registers the Slack setter: it names the session from the delivery's auth attributes. The
|
|
22
22
|
// person's next message sets `processing`, then parks `active` unless it reports again that it
|
|
23
23
|
// waits on the person. A job's result wake names no session, so its turn sets no state.
|
|
24
|
+
// • THE WORKING SIGNS (OPE-2629). The same setter shows the 👀 and the status line on the session's
|
|
25
|
+
// root while a turn works, and clears both at the park. See `setSlackDmWorkingSigns`.
|
|
24
26
|
// • A FORWARDED JOB's WAIT (OPE-2536). A job the DM seat forwards to the work session can end waiting
|
|
25
27
|
// on the person: it asked a question in the room (`ask_room`), or it needs their own sign-in. The
|
|
26
28
|
// work session has no `update_status`, so the host sets the session `suspended` at the job's
|
|
@@ -38,6 +40,8 @@
|
|
|
38
40
|
import {
|
|
39
41
|
cancelAnchoredTurn,
|
|
40
42
|
resolvePartition,
|
|
43
|
+
STATUS_REACTION,
|
|
44
|
+
swallowRoomOp,
|
|
41
45
|
turnAnchorKey,
|
|
42
46
|
turnChannelKey,
|
|
43
47
|
type AttachSessionForCancel,
|
|
@@ -59,6 +63,13 @@ export type SlackSessionStatus = "processing" | "active" | "suspended";
|
|
|
59
63
|
/** The auth attribute the DM door stamps with the session's thread root (slack-front.ts `scheduleDmWake`). */
|
|
60
64
|
const AGENT_SESSION_TS_ATTRIBUTE = "agent_session_ts";
|
|
61
65
|
|
|
66
|
+
/**
|
|
67
|
+
* The mark a PINNED bag carries (OPE-2629). A forwarded job's settlement sets the session's state from
|
|
68
|
+
* its pin, not from a turn, so the setter must not touch the turn's working signs for it: a turn of the
|
|
69
|
+
* same session can be running at that moment, and its 👀 must stay until its own park.
|
|
70
|
+
*/
|
|
71
|
+
const SESSION_PIN_ATTRIBUTE = "oe_session_pin";
|
|
72
|
+
|
|
62
73
|
/** The form-call seam: `slackForm` in production, a recorder in the tests. */
|
|
63
74
|
export type SlackFormCall = (method: string, body: string) => Promise<Record<string, unknown> | undefined>;
|
|
64
75
|
|
|
@@ -155,16 +166,94 @@ export const slackSurfaceSessionPin: SurfaceSessionPinner = (attributes) => {
|
|
|
155
166
|
if (!session) return undefined;
|
|
156
167
|
return {
|
|
157
168
|
id: `${session.channelId}:${session.threadTs}`,
|
|
158
|
-
attributes: { channel_id: session.channelId, channel_type: "im", [AGENT_SESSION_TS_ATTRIBUTE]: session.threadTs },
|
|
169
|
+
attributes: { channel_id: session.channelId, channel_type: "im", [AGENT_SESSION_TS_ATTRIBUTE]: session.threadTs, [SESSION_PIN_ATTRIBUTE]: "1" },
|
|
159
170
|
};
|
|
160
171
|
};
|
|
161
172
|
|
|
173
|
+
// --- the working signs ------------------------------------------------------------------------------
|
|
174
|
+
//
|
|
175
|
+
// 🔴 THE DM SEAT'S 👀 AND STATUS LINE LIVE HERE (OPE-2629). A 1:1 DM runs on the `unified` channel, so
|
|
176
|
+
// none of the Slack channel's own handlers reach it: `onInbound` (the 👀) and the `turn.started`
|
|
177
|
+
// renderer (the status line) serve only eve's native mention door. Before this, a person who DMed
|
|
178
|
+
// the app saw neither during the turn. Core's turn-anchor hook calls this setter at every DM turn's
|
|
179
|
+
// start (`processing`) and park (`active` or `suspended`), so the signs ride the same two calls:
|
|
180
|
+
// • 👀 on the session's thread root — the message itself when it is top-level, the same root the
|
|
181
|
+
// mention door's 👀 uses for a threaded one. Added at `processing`, removed at the park.
|
|
182
|
+
// • Slack's assistant status (`assistant.threads.setStatus`) on that root: `Working...` at
|
|
183
|
+
// `processing`, cleared at the park. The flat DM reply does not clear it, so the park must.
|
|
184
|
+
// A forwarded job's settlement (OPE-2536) also calls this setter, from the job's PIN. A turn of the
|
|
185
|
+
// same session can run at that moment, so a pinned bag (`SESSION_PIN_ATTRIBUTE`) moves the session
|
|
186
|
+
// state only and leaves the signs to the turn's own park.
|
|
187
|
+
// A turn that fails for good emits `session.failed` and never parks, so its signs stay: the 👀 until
|
|
188
|
+
// the session's next turn parks, and the status line until Slack expires it.
|
|
189
|
+
|
|
190
|
+
/** The status line a DM turn shows while it works — the same words the mention door's `turn.started` shows. */
|
|
191
|
+
export const SLACK_DM_WORKING_STATUS = "Working...";
|
|
192
|
+
|
|
193
|
+
/** The 👀 the bot sets while it works (core status/status.ts `STATUS_REACTION.working`). */
|
|
194
|
+
const WORKING_REACTION = STATUS_REACTION.working;
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* What an operator does when one sign call fails with a setup error (`SIGN_SETUP_ERRORS`). The sign
|
|
198
|
+
* needs a scope or the agent view, and both come from the app manifest, so the fix is the manifest
|
|
199
|
+
* paste. Logged once per method and error, so a workspace without the feature does not log it on
|
|
200
|
+
* every turn.
|
|
201
|
+
*/
|
|
202
|
+
const SIGN_SETUP_FIX: Readonly<Record<string, string>> = {
|
|
203
|
+
"reactions.add": "the 👀 needs the reactions:write bot scope",
|
|
204
|
+
"reactions.remove": "the 👀 needs the reactions:write bot scope",
|
|
205
|
+
"assistant.threads.setStatus": "the DM status line needs the assistant:write bot scope and the agent view (features.agent_view)",
|
|
206
|
+
};
|
|
207
|
+
/** Slack's answers that mean the app lacks a scope or a feature: a setup gap, not a passing fault. */
|
|
208
|
+
const SIGN_SETUP_ERRORS = new Set(["missing_scope", "feature_disabled", "not_allowed_token_type", "not_allowed", "access_denied"]);
|
|
209
|
+
/** Slack's answers that say the sign is already in the state asked for. */
|
|
210
|
+
const SIGN_NO_OP_ERRORS = new Set(["already_reacted", "no_reaction"]);
|
|
211
|
+
const warnedSignErrors = new Set<string>();
|
|
212
|
+
|
|
213
|
+
/** One sign call. Never throws; a failure is logged once per method and error, with the setup fix. */
|
|
214
|
+
async function sendSign(form: SlackFormCall, method: string, body: URLSearchParams): Promise<void> {
|
|
215
|
+
let error: string;
|
|
216
|
+
try {
|
|
217
|
+
const res = await form(method, body.toString());
|
|
218
|
+
if (res?.ok === true) return;
|
|
219
|
+
error = String(res?.error ?? "no answer");
|
|
220
|
+
} catch (err) {
|
|
221
|
+
error = String(err);
|
|
222
|
+
}
|
|
223
|
+
if (SIGN_NO_OP_ERRORS.has(error)) return;
|
|
224
|
+
const key = `${method}:${error}`;
|
|
225
|
+
if (warnedSignErrors.has(key)) return;
|
|
226
|
+
warnedSignErrors.add(key);
|
|
227
|
+
const fix = SIGN_SETUP_ERRORS.has(error)
|
|
228
|
+
? `${SIGN_SETUP_FIX[method] ?? "this sign needs a manifest scope"}. Fix: paste the app manifest again (App Manifest → Save Changes) and reinstall if Slack asks`
|
|
229
|
+
: "the sign is best-effort and the turn goes on";
|
|
230
|
+
console.warn(`[open-everywhere:slack-session] ${method} failed (${error}) — ${fix}. Logged once per process.`);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/** Test-only: forget which sign failures were already logged. */
|
|
234
|
+
export function _resetSignWarningsForTest(): void {
|
|
235
|
+
warnedSignErrors.clear();
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Show or clear the DM turn's working signs on the session's root: the 👀 and the status line. The
|
|
240
|
+
* two calls run in parallel, because the turn waits on this setter. A swallowed room op (the egress
|
|
241
|
+
* spoof) sends neither. Never throws.
|
|
242
|
+
*/
|
|
243
|
+
async function setSlackDmWorkingSigns(session: { readonly channelId: string; readonly threadTs: string }, working: boolean, form: SlackFormCall = slackForm): Promise<void> {
|
|
244
|
+
if (swallowRoomOp(`a slack DM working sign ${working ? "add" : "remove"}`)) return;
|
|
245
|
+
const reaction = new URLSearchParams({ channel: session.channelId, name: WORKING_REACTION, timestamp: session.threadTs });
|
|
246
|
+
const status = new URLSearchParams({ channel_id: session.channelId, thread_ts: session.threadTs, status: working ? SLACK_DM_WORKING_STATUS : "" });
|
|
247
|
+
await Promise.all([sendSign(form, working ? "reactions.add" : "reactions.remove", reaction), sendSign(form, "assistant.threads.setStatus", status)]);
|
|
248
|
+
}
|
|
249
|
+
|
|
162
250
|
/** The Slack setter core's turn-anchor hook calls (registered in register.ts). */
|
|
163
251
|
export function slackSurfaceSessionSetter(form: SlackFormCall = slackForm): SurfaceSessionSetter {
|
|
164
252
|
return async (attributes, status) => {
|
|
165
253
|
const session = slackAgentSessionOf(attributes);
|
|
166
254
|
if (!session) return undefined;
|
|
167
|
-
|
|
255
|
+
const fromTurn = attributes[SESSION_PIN_ATTRIBUTE] === undefined;
|
|
256
|
+
await Promise.all([setSlackSessionStatus({ ...session, status }, form), ...(fromTurn ? [setSlackDmWorkingSigns(session, status === "processing", form)] : [])]);
|
|
168
257
|
return `${session.channelId}:${session.threadTs}`;
|
|
169
258
|
};
|
|
170
259
|
}
|
|
@@ -103,6 +103,7 @@ import {
|
|
|
103
103
|
type EngagementRow,
|
|
104
104
|
type ChannelSessionRow,
|
|
105
105
|
type RoomRoster,
|
|
106
|
+
type RoomShape,
|
|
106
107
|
type ContextLine,
|
|
107
108
|
type WakeAttachment,
|
|
108
109
|
type WakeMessageInput,
|
|
@@ -120,7 +121,9 @@ import {
|
|
|
120
121
|
slackMode,
|
|
121
122
|
buildSlackAuth,
|
|
122
123
|
channelFactsFromInfo,
|
|
124
|
+
imRosterFactsFromInfo,
|
|
123
125
|
partnerFactsFromInfo,
|
|
126
|
+
slackUserGuestFlagged,
|
|
124
127
|
appCanReadConversation,
|
|
125
128
|
type ChannelFacts,
|
|
126
129
|
type OpenEverywhereSlackAuth,
|
|
@@ -3785,13 +3788,16 @@ function slackRosterFailureState(res: Record<string, unknown> | undefined): "uno
|
|
|
3785
3788
|
/**
|
|
3786
3789
|
* Live Slack roster for a governance channel's floor tripwire (OPE-110, access/audience-floor.ts).
|
|
3787
3790
|
* Up to three Slack reads: conversations.info (is_private + externally-shared bit),
|
|
3788
|
-
* conversations.members (member ids) and auth.test (the bot's own user id).
|
|
3791
|
+
* conversations.members (member ids) and auth.test (the bot's own user id). A 1:1 DM adds a fourth,
|
|
3792
|
+
* users.info on the partner, and reads neither `is_org_shared` nor `is_shared` (OPE-2630, {@link slackImRoster}). The roster excludes the
|
|
3789
3793
|
* bot's own user id, so the agent doesn't count as a non-admin member. A failed conversations.info
|
|
3790
3794
|
* or conversations.members read, a members list of a NON-SHARED channel longer than one page, or a
|
|
3791
3795
|
* thrown error returns `resolved: false`, with an `unresolvedState` (OPE-2306). A transient failure
|
|
3792
3796
|
* is `failed`: a non-ok answer outside SLACK_ROSTER_UNOBSERVABLE_ERRORS, no answer, a thrown error, a
|
|
3793
3797
|
* conversations.info answer with no channel object. A steady state is `unobservable`: a code in that
|
|
3794
|
-
* set, or a non-shared members list longer than one page.
|
|
3798
|
+
* set, or a non-shared members list longer than one page. For a 1:1 DM, a failed partner users.info
|
|
3799
|
+
* read also returns `resolved: false`, with the same split ({@link slackImRoster}): so a short users.info
|
|
3800
|
+
* outage makes the connector-disclosure floor refuse in every 1:1 DM for up to 30 s. A failed auth.test does not unresolve the roster: the roster stays resolved and counts the bot as a
|
|
3795
3801
|
* non-admin member, so the governance channel does not arm. `resolved: false` is closed for the
|
|
3796
3802
|
* governance tripwire: the floor computes to `guest`, so the channel can never arm. It is
|
|
3797
3803
|
* also closed for the clicker floor's beneficiary leg, which refuses on it (`unattested-room`). It
|
|
@@ -3803,7 +3809,8 @@ function slackRosterFailureState(res: Record<string, unknown> | undefined): "uno
|
|
|
3803
3809
|
* answer for UNRESOLVED_ROSTER_TTL_MS (30 s) only, not for the full memo TTL (OPE-2150), so one
|
|
3804
3810
|
* failed read disarms the inert legs, and refuses the connector-disclosure floor, for at most that
|
|
3805
3811
|
* short window.
|
|
3806
|
-
* `hasGuest` uses the channel's externally-shared bit as the available external signal
|
|
3812
|
+
* `hasGuest` uses the channel's externally-shared bit as the available external signal (a 1:1 DM
|
|
3813
|
+
* reads the Slack Connect bits and the partner's guest flags instead, {@link slackImRoster});
|
|
3807
3814
|
* per-member guest classification rides the guest-detection work (design §2/§10 fragile-point #1) and
|
|
3808
3815
|
* is a future tightening, not a loosening (an internal-only private channel already needs every
|
|
3809
3816
|
* member on the roster to arm). Governance channels are small by config lint, so a single members
|
|
@@ -3834,7 +3841,7 @@ export async function slackGovernanceRoster(channelId: string): Promise<RoomRost
|
|
|
3834
3841
|
// with `hasGuest: true` and an EMPTY member list, never a partial one. The floor is `guest` for every consumer, and
|
|
3835
3842
|
// the observed-guest legs refuse. The share bit is a steady state, so the full memo TTL is fine.
|
|
3836
3843
|
if (facts.trustChecked === true && facts.isExternal === true) {
|
|
3837
|
-
return { humanMemberIds: [], resolved: true, isPrivate: facts.isPrivate === true, hasGuest: true };
|
|
3844
|
+
return { humanMemberIds: [], resolved: true, isPrivate: facts.isPrivate === true, hasGuest: true, ...slackRoomShape(facts) };
|
|
3838
3845
|
}
|
|
3839
3846
|
// An ok conversations.info with no channel object is an odd answer, not a steady state: it is
|
|
3840
3847
|
// `failed`, as on the one-page path below.
|
|
@@ -3853,6 +3860,8 @@ export async function slackGovernanceRoster(channelId: string): Promise<RoomRost
|
|
|
3853
3860
|
const humanMemberIds = (members.members as unknown[])
|
|
3854
3861
|
.filter((m): m is string => typeof m === "string")
|
|
3855
3862
|
.filter((m) => m !== botId);
|
|
3863
|
+
const im = imRosterFactsFromInfo(info);
|
|
3864
|
+
if (im) return await slackImRoster(im, humanMemberIds, facts.isPrivate === true);
|
|
3856
3865
|
return {
|
|
3857
3866
|
humanMemberIds,
|
|
3858
3867
|
resolved: facts.trustChecked === true, // conversations.info authoritative
|
|
@@ -3860,12 +3869,57 @@ export async function slackGovernanceRoster(channelId: string): Promise<RoomRost
|
|
|
3860
3869
|
...(facts.trustChecked === true ? {} : { unresolvedState: "failed" as const }),
|
|
3861
3870
|
isPrivate: facts.isPrivate === true,
|
|
3862
3871
|
hasGuest: facts.isExternal === true,
|
|
3872
|
+
...slackRoomShape(facts),
|
|
3863
3873
|
};
|
|
3864
3874
|
} catch {
|
|
3865
3875
|
return { humanMemberIds: [], resolved: false, unresolvedState: "failed", isPrivate: false, hasGuest: false };
|
|
3866
3876
|
}
|
|
3867
3877
|
}
|
|
3868
3878
|
|
|
3879
|
+
/**
|
|
3880
|
+
* The roster of a 1:1 DM (OPE-2630). A DM has two parties, the app and one person, so its audience is
|
|
3881
|
+
* that person. `hasGuest` is true when Slack OBSERVES an outside party: a Slack Connect DM
|
|
3882
|
+
* (`is_ext_shared` / `is_pending_ext_shared`), or a partner whose `users.info` carries a guest flag
|
|
3883
|
+
* (`slackUserGuestFlagged`). `is_org_shared` and `is_shared` do not count. `is_org_shared` marks a
|
|
3884
|
+
* share across the Grid's own workspaces. `is_shared` marks any share, so a Connect DM sets it too, but
|
|
3885
|
+
* a Connect DM also carries the Connect bits read here. Reading the two as a guest refused every
|
|
3886
|
+
* approval card in a full member's own Grid DM (`imRosterFactsFromInfo` holds the reason).
|
|
3887
|
+
*
|
|
3888
|
+
* The workspace rung walk (`partnerFactsFromInfo`) is NOT read here. An undeclared Grid workspace is
|
|
3889
|
+
* a guest by INFERENCE (OPE-1508), not by observation, and the observed-guest legs of the floor refuse
|
|
3890
|
+
* only an observation (access/audience-floor.ts, the polarity rule). Reading the walk here would
|
|
3891
|
+
* floor every DM of an undeclared Grid to `guest` again.
|
|
3892
|
+
*
|
|
3893
|
+
* A failed `users.info` read does not resolve the roster: `failed` on a transient error, and
|
|
3894
|
+
* `unobservable` on a steady one (`missing_scope`), the same split as the room reads above.
|
|
3895
|
+
*/
|
|
3896
|
+
async function slackImRoster(
|
|
3897
|
+
im: { readonly partnerId?: string; readonly connectShared: boolean },
|
|
3898
|
+
humanMemberIds: readonly string[],
|
|
3899
|
+
isPrivate: boolean,
|
|
3900
|
+
): Promise<RoomRoster> {
|
|
3901
|
+
if (im.connectShared) return { humanMemberIds, resolved: true, isPrivate, hasGuest: true, room: "dm" };
|
|
3902
|
+
const partnerId = im.partnerId ?? (humanMemberIds.length === 1 ? humanMemberIds[0] : undefined);
|
|
3903
|
+
// A DM that names no partner gives no person to ask about. The roster then rests on the Connect
|
|
3904
|
+
// bits alone (read above): resolved, with no outside party observed. Before OPE-2630 a DM like this
|
|
3905
|
+
// with `is_org_shared` or `is_shared` set floored to `guest`; now it does not.
|
|
3906
|
+
if (partnerId === undefined) return { humanMemberIds, resolved: true, isPrivate, hasGuest: false, room: "dm" };
|
|
3907
|
+
const who = await slackForm("users.info", `user=${encodeURIComponent(partnerId)}`);
|
|
3908
|
+
const user = who?.ok === true ? (who.user as Record<string, unknown> | undefined) : undefined;
|
|
3909
|
+
if (!user || typeof user !== "object") {
|
|
3910
|
+
return { humanMemberIds: [], resolved: false, unresolvedState: who?.ok === true ? "failed" : slackRosterFailureState(who), isPrivate: false, hasGuest: false };
|
|
3911
|
+
}
|
|
3912
|
+
return { humanMemberIds, resolved: true, isPrivate, hasGuest: slackUserGuestFlagged(user), room: "dm" };
|
|
3913
|
+
}
|
|
3914
|
+
|
|
3915
|
+
/** The roster's `room` from a `conversations.info` channel type, or nothing when Slack did not say. */
|
|
3916
|
+
function slackRoomShape(facts: ChannelFacts): { readonly room?: RoomShape } {
|
|
3917
|
+
if (facts.channelType === "im") return { room: "dm" };
|
|
3918
|
+
if (facts.channelType === "mpim") return { room: "group-dm" };
|
|
3919
|
+
if (facts.channelType === "channel" || facts.channelType === "group") return { room: "channel" };
|
|
3920
|
+
return {};
|
|
3921
|
+
}
|
|
3922
|
+
|
|
3869
3923
|
/**
|
|
3870
3924
|
* Register {@link slackGovernanceRoster} as the governance floor-tripwire member supplier (OPE-110).
|
|
3871
3925
|
* Core owns the seam + the fail-closed default; this is the Slack arm, wired at boot from
|
|
@@ -265,7 +265,7 @@ function slackWorkspaceVouched(teamId: string | undefined, enterpriseId: string
|
|
|
265
265
|
export function partnerFactsFromInfo(info: unknown, expectedTeamId?: string): { dmPartnerTrust: "internal" | "external" } {
|
|
266
266
|
const u = (info as { user?: Record<string, unknown> } | null)?.user;
|
|
267
267
|
if (!u || typeof u !== "object") return { dmPartnerTrust: "external" }; // lookup failed → fail closed
|
|
268
|
-
const guestOrStranger =
|
|
268
|
+
const guestOrStranger = slackUserGuestFlagged(u);
|
|
269
269
|
const deleted = Boolean(u["deleted"]);
|
|
270
270
|
const homeTeamId = typeof u["team_id"] === "string" ? (u["team_id"] as string) : undefined;
|
|
271
271
|
// 🔴 NO EXPECTED WORKSPACE IS NOT AN OWN-WORKSPACE ANSWER. This used to read the absence as
|
|
@@ -288,6 +288,39 @@ export function partnerFactsFromInfo(info: unknown, expectedTeamId?: string): {
|
|
|
288
288
|
return { dmPartnerTrust: internal ? "internal" : "external" };
|
|
289
289
|
}
|
|
290
290
|
|
|
291
|
+
/** Slack's own guest flags on a `users.info` user object: a multi-channel guest (`is_restricted`), a
|
|
292
|
+
* single-channel guest (`is_ultra_restricted`) or a Slack Connect stranger (`is_stranger`). The ONE
|
|
293
|
+
* reader of these three flags, so {@link partnerFactsFromInfo} and the 1:1 DM roster
|
|
294
|
+
* ({@link imRosterFactsFromInfo}) cannot disagree on what Slack calls a guest. It reads no workspace
|
|
295
|
+
* and no declaration: it is what Slack OBSERVED about the person, nothing inferred. */
|
|
296
|
+
export function slackUserGuestFlagged(u: Record<string, unknown>): boolean {
|
|
297
|
+
return Boolean(u["is_restricted"] || u["is_ultra_restricted"] || u["is_stranger"]);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* The room facts of a 1:1 DM (`is_im`) from a `conversations.info` response, or `undefined` for any
|
|
302
|
+
* other conversation (OPE-2630). The DM roster reads these instead of
|
|
303
|
+
* {@link ChannelFacts.isExternal}.
|
|
304
|
+
*
|
|
305
|
+
* 🔴 WHY A DM DOES NOT READ `isExternal`. `isExternal` is true on ANY of the four share bits.
|
|
306
|
+
* `is_org_shared` marks a share across the workspaces of ONE Enterprise Grid organisation. `is_shared`
|
|
307
|
+
* marks ANY share, a Slack Connect one included, so it alone does not tell a Grid share from a Connect
|
|
308
|
+
* one. A Connect DM also carries `is_ext_shared` (or `is_pending_ext_shared`), and this function reads
|
|
309
|
+
* those two bits. A 1:1 DM has exactly two parties, the app and one person, so `is_org_shared` or `is_shared` on it
|
|
310
|
+
* says nothing about an outside audience. Read as "external", they floored a full member's own DM to
|
|
311
|
+
* `guest`, and the card-disclosure floor refused every approval card there (OPE-2630, a Grid install).
|
|
312
|
+
* So a DM's audience is judged by the two facts that do name an outside party: the Slack Connect bits
|
|
313
|
+
* (`is_ext_shared`, `is_pending_ext_shared`) here, and the partner's own guest flags
|
|
314
|
+
* ({@link slackUserGuestFlagged}), which the roster reads with `users.info`.
|
|
315
|
+
*/
|
|
316
|
+
export function imRosterFactsFromInfo(info: unknown): { readonly partnerId?: string; readonly connectShared: boolean } | undefined {
|
|
317
|
+
const ch = (info as { channel?: Record<string, unknown> } | null)?.channel;
|
|
318
|
+
if (!ch || typeof ch !== "object" || ch["is_im"] !== true) return undefined;
|
|
319
|
+
const partnerId = typeof ch["user"] === "string" && ch["user"].length > 0 ? (ch["user"] as string) : undefined;
|
|
320
|
+
const connectShared = Boolean(ch["is_ext_shared"] || ch["is_pending_ext_shared"]);
|
|
321
|
+
return { ...(partnerId === undefined ? {} : { partnerId }), connectShared };
|
|
322
|
+
}
|
|
323
|
+
|
|
291
324
|
/** What an app_mention envelope tells us directly (often just channel_type). */
|
|
292
325
|
export function channelFactsFromRaw(raw: Record<string, unknown>): ChannelFacts {
|
|
293
326
|
const channelType = typeof raw["channel_type"] === "string" ? (raw["channel_type"] as string) : undefined;
|
package/agent/docs-generated.ts
CHANGED
|
@@ -154,6 +154,15 @@ export const PROVIDER_DOCS: readonly DocEntry[] = [
|
|
|
154
154
|
"the line they read as members. A workspace under a **different** Grid, or under no declaration, is",
|
|
155
155
|
"still an outside author.",
|
|
156
156
|
"",
|
|
157
|
+
"**An approval card in a 1:1 DM does not read the line (OPE-2630).** On a Grid, Slack can mark a DM",
|
|
158
|
+
"with `is_org_shared` (a share across the Grid's own workspaces) and `is_shared` (any share, a Slack",
|
|
159
|
+
"Connect one included). A DM does not count these two bits as an outside audience. A DM refuses an",
|
|
160
|
+
"approval card only on what Slack observes: the Slack Connect bits (`is_ext_shared`,",
|
|
161
|
+
"`is_pending_ext_shared`), or a partner whose `users.info` carries a guest flag. A group DM and a",
|
|
162
|
+
"channel still count all four share bits. The",
|
|
163
|
+
"**click** on that card is a member question, so it does read the line: a member-tier click from a",
|
|
164
|
+
"person whose home workspace is not the click's workspace counts as a guest click without it.",
|
|
165
|
+
"",
|
|
157
166
|
"The member half needs no extra scope and no configuration beyond the line above: Open Everywhere",
|
|
158
167
|
"reads the author's own workspace and Grid from the `users.info` lookup it already pays for, so it",
|
|
159
168
|
"holds on every ingress, the channel-mention path included. A single-workspace install has no",
|
|
@@ -733,7 +742,10 @@ export const PROVIDER_DOCS: readonly DocEntry[] = [
|
|
|
733
742
|
" reaction via the `react` tool — a bare emoji ack (e.g. 👍) instead of a low-content reply in a",
|
|
734
743
|
" busy thread. It's a genuine alternative response, not a status indicator, and is audited like",
|
|
735
744
|
" any other tool call.",
|
|
736
|
-
"4. Test a DM: open a direct message with the bot and send a plain message (no `@mention` needed in a DM).
|
|
745
|
+
"4. Test a DM: open a direct message with the bot and send a plain message (no `@mention` needed in a DM). The DM surface's memory is isolated and `private`. While the turn works, the bot shows the same two signs a mention gets:",
|
|
746
|
+
" - **👀 on your message.** A message inside a DM thread gets it on the thread's first message, as in a channel. It goes away when the turn ends, unless the turn fails for good: then the 👀 stays until the next turn of that conversation ends. A DM reply stays flat, as described above.",
|
|
747
|
+
" - **The status line** `Working...` under your message, removed when the turn ends. A turn that fails for good leaves it until Slack expires it. A DM turn shows no live step labels: those come from the Slack channel's own handlers, and a DM runs on the DM seat instead.",
|
|
748
|
+
" - **If a sign is missing**, the app lacks a scope or the agent view. The 👀 needs `reactions:write`. The status line needs `assistant:write` and the agent view (`features.agent_view`). All three come from the manifest: paste it again (App Manifest → Save Changes) and reinstall if Slack asks. The deployment logs the refused call once per process, with this fix in the line: search the logs for `[open-everywhere:slack-session]`.",
|
|
737
749
|
"",
|
|
738
750
|
"### What it can read on Slack",
|
|
739
751
|
"",
|
|
@@ -990,6 +1002,7 @@ export const PROVIDER_DOCS: readonly DocEntry[] = [
|
|
|
990
1002
|
"| **A title.** A conversation that opens with a top-level message shows a short title from the words the person typed. | The DM door creates the session with that title before the turn starts. The title is the first typed line, cut to 60 characters at a word. A member mention is dropped, a channel shows as `#name` and a link shows as its label, so the title holds no raw id. The lines of a forwarded or quoted message are another conversation's words, so they never make the title: a forward with no typed text gets no title. A voice clip's transcript is not typed text either, so a clip with no typed words gets no title. The host writes the title in code; no model writes it. |",
|
|
991
1003
|
"| **Their own title.** The person can rename a session at any time. | Slack applies a title only when a call creates the session, so OE never overwrites a rename. |",
|
|
992
1004
|
"| **Working, with a Stop button.** | A turn's start sets the session `processing`. Slack then shows its Stop button, because the app subscribes to `agent_session_stopped`. |",
|
|
1005
|
+
"| **👀 and the status line.** | The same start puts 👀 on the session's first message and shows `Working...` as Slack's assistant status there. The park removes both. The DM seat runs on the `unified` channel, so the Slack channel's own 👀 and status line (the mention door's) never reach it: these two calls are the DM's only working signs (OPE-2629). A forwarded job's settlement (next rows) sets the session state only and leaves the signs to the running turn. A turn that fails for good never parks, so its signs stay. |",
|
|
993
1006
|
"| **Stop.** | A press takes the same road as the ✋ reaction and `/oe stop`: it is decided at ingress, before any model call, so it is free and works over a spend cap. It stops the turn of the session's thread. In the DM it falls back to the DM's newest turn, because a top-level DM message keys its turn by the channel alone. The audit row is `turn.cancel_requested` with `via: \"stop-button\"`. Slack does not move the status itself, so OE sets the session `active`. |",
|
|
994
1007
|
"| **\"Needs you\".** | When the seat's turn reported through `update_status` that it waits on the person (`needs_input` or `blocked`), the agent session parks `suspended`. Otherwise it parks `active`. The person's next message clears it: that turn sets `processing`, then parks `active` unless it reports again that it waits on the person, or a job of the session still waits (next row). A job's result turn sets no state: the result wake names no agent session. |",
|
|
995
1008
|
"| **\"Needs you\", for a job.** | A job the DM seat forwards can end waiting on the person: its worker asked a question in the DM (`ask_room`) and finished, or it needs the person's own sign-in. The work session has no `update_status`, so the host sets the agent session the job came from `suspended` when the job's task settles. While the job waits, every turn of that session parks `suspended`. When the person answers and the seat forwards the answer to the same task, the job moves on in the session it came from, whichever agent session of the DM the answer came from. That session then parks `active`, unless another of its jobs still waits. After a sign-in, the resume forward sets the session `active`. The audit row is `work.surface_wait`: ids, the state and the reason, never the question. |",
|
|
@@ -1033,7 +1046,11 @@ export const PROVIDER_DOCS: readonly DocEntry[] = [
|
|
|
1033
1046
|
"test `tests/integration/channels/slack-agent-session.test.ts` drives a booted agent through the",
|
|
1034
1047
|
"signed DM door: the title before `processing`, `suspended` after `update_status(needs_input)`,",
|
|
1035
1048
|
"`active` after the answer, no title for a forward with no typed text, and a signed Stop press that",
|
|
1036
|
-
"stops a held turn and sets the session `active`. The
|
|
1049
|
+
"stops a held turn and sets the session `active`. The same test proves the working signs: 👀 on the",
|
|
1050
|
+
"person's top-level message before the park, `Working...` on the same message, and both cleared at the",
|
|
1051
|
+
"park. The live e2e scenario `S-SESSION-STOP` reads `reactions.get` on the person's real DM message",
|
|
1052
|
+
"while its turn runs (👀 present), and again after the stop (👀 gone). No API reads the status line",
|
|
1053
|
+
"back, so no live leg proves it. The integration test",
|
|
1037
1054
|
"`tests/integration/channels/slack-job-wait.test.ts` forwards a DM job whose worker asks with",
|
|
1038
1055
|
"`ask_room`: the session goes `suspended` at the job's settlement, another message keeps it so, and the",
|
|
1039
1056
|
"answer forwarded to the same task parks it `active`. The test holds the seat's result-wake",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-manifest.d.ts","sourceRoot":"","sources":["../../agent/app-manifest.ts"],"names":[],"mappings":"AAeA;;mEAEmE;AACnE,eAAO,MAAM,6BAA6B,oBAAoB,CAAC;AAE/D;yFACyF;AACzF,eAAO,MAAM,sBAAsB,oBAAoB,CAAC;AAKxD,4EAA4E;AAC5E,eAAO,MAAM,uBAAuB,KAAK,CAAC;AAE1C,oGAAoG;AACpG,eAAO,MAAM,kBAAkB,KAAK,CAAC;AAErC;0GAC0G;AAC1G,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAKnE;AAED;;kFAEkF;AAClF,MAAM,WAAW,uBAAuB;IACtC;;yBAEqB;IACrB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B;;0CAEsC;IACtC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B;8CAC0C;IAC1C,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B;;;;;OAKG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC;IAC/B;;;;;OAKG;IACH,QAAQ,CAAC,gBAAgB,CAAC,EAAE,SAAS;QAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAC7F;AAED,8FAA8F;AAC9F,eAAO,MAAM,2BAA2B,MAAM,CAAC;AAU/C;;6DAE6D;AAC7D,eAAO,MAAM,oCAAoC,uBAAuB,CAAC;AAEzE;iGACiG;AACjG,wBAAgB,oBAAoB,CAAC,IAAI,GAAE,uBAA4B,GAAG,MAAM,CAM/E;AAQD;+DAC+D;AAC/D,wBAAgB,wBAAwB,CAAC,IAAI,GAAE,uBAA4B,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"app-manifest.d.ts","sourceRoot":"","sources":["../../agent/app-manifest.ts"],"names":[],"mappings":"AAeA;;mEAEmE;AACnE,eAAO,MAAM,6BAA6B,oBAAoB,CAAC;AAE/D;yFACyF;AACzF,eAAO,MAAM,sBAAsB,oBAAoB,CAAC;AAKxD,4EAA4E;AAC5E,eAAO,MAAM,uBAAuB,KAAK,CAAC;AAE1C,oGAAoG;AACpG,eAAO,MAAM,kBAAkB,KAAK,CAAC;AAErC;0GAC0G;AAC1G,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAKnE;AAED;;kFAEkF;AAClF,MAAM,WAAW,uBAAuB;IACtC;;yBAEqB;IACrB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B;;0CAEsC;IACtC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B;8CAC0C;IAC1C,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B;;;;;OAKG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC;IAC/B;;;;;OAKG;IACH,QAAQ,CAAC,gBAAgB,CAAC,EAAE,SAAS;QAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAC7F;AAED,8FAA8F;AAC9F,eAAO,MAAM,2BAA2B,MAAM,CAAC;AAU/C;;6DAE6D;AAC7D,eAAO,MAAM,oCAAoC,uBAAuB,CAAC;AAEzE;iGACiG;AACjG,wBAAgB,oBAAoB,CAAC,IAAI,GAAE,uBAA4B,GAAG,MAAM,CAM/E;AAQD;+DAC+D;AAC/D,wBAAgB,wBAAwB,CAAC,IAAI,GAAE,uBAA4B,GAAG,MAAM,CAwNnF;AAED,8CAA8C;AAC9C,eAAO,MAAM,qBAAqB,+BAA+B,CAAC;AAElE,kFAAkF;AAClF,eAAO,MAAM,wBAAwB,EAAE,SAAS,MAAM,EAQrD,CAAC;AAEF;;8BAE8B;AAC9B,eAAO,MAAM,0BAA0B,EAAE,SAAS,MAAM,EAKvD,CAAC"}
|
|
@@ -211,7 +211,9 @@ export function slackAppManifestTemplate(opts = {}) {
|
|
|
211
211
|
"lists:read",
|
|
212
212
|
"lists:write",
|
|
213
213
|
// The side panel (OPE-2457): Slack requires it for `app_context_changed`, the event whose
|
|
214
|
-
// subscription makes Slack attach the viewed room to each side-panel `message.im`.
|
|
214
|
+
// subscription makes Slack attach the viewed room to each side-panel `message.im`. It also
|
|
215
|
+
// covers `assistant.threads.setStatus`, the status line a turn shows while it works: the
|
|
216
|
+
// mention door's, and the DM seat's (OPE-2629, channels/slack-agent-session.ts).
|
|
215
217
|
"assistant:write",
|
|
216
218
|
// Slack requires this bot scope on an app whose people connect to Slack's MCP server
|
|
217
219
|
// (OPE-2421). Declared only when the app declares "search Slack as you".
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-manifest.js","sourceRoot":"","sources":["../../agent/app-manifest.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"app-manifest.js","sourceRoot":"","sources":["../../agent/app-manifest.ts"],"names":[],"mappings":"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"}
|
|
@@ -44,6 +44,10 @@ export declare function slackAgentSessionOf(attributes: Readonly<Record<string,
|
|
|
44
44
|
* `suspended` long after the delivery's auth is gone. Ids only, never text. Pure.
|
|
45
45
|
*/
|
|
46
46
|
export declare const slackSurfaceSessionPin: SurfaceSessionPinner;
|
|
47
|
+
/** The status line a DM turn shows while it works — the same words the mention door's `turn.started` shows. */
|
|
48
|
+
export declare const SLACK_DM_WORKING_STATUS = "Working...";
|
|
49
|
+
/** Test-only: forget which sign failures were already logged. */
|
|
50
|
+
export declare function _resetSignWarningsForTest(): void;
|
|
47
51
|
/** The Slack setter core's turn-anchor hook calls (registered in register.ts). */
|
|
48
52
|
export declare function slackSurfaceSessionSetter(form?: SlackFormCall): SurfaceSessionSetter;
|
|
49
53
|
/** One press of the Stop button, as the route reads it. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"slack-agent-session.d.ts","sourceRoot":"","sources":["../../../agent/channels/slack-agent-session.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"slack-agent-session.d.ts","sourceRoot":"","sources":["../../../agent/channels/slack-agent-session.ts"],"names":[],"mappings":"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"}
|
|
@@ -21,6 +21,8 @@
|
|
|
21
21
|
// registers the Slack setter: it names the session from the delivery's auth attributes. The
|
|
22
22
|
// person's next message sets `processing`, then parks `active` unless it reports again that it
|
|
23
23
|
// waits on the person. A job's result wake names no session, so its turn sets no state.
|
|
24
|
+
// • THE WORKING SIGNS (OPE-2629). The same setter shows the 👀 and the status line on the session's
|
|
25
|
+
// root while a turn works, and clears both at the park. See `setSlackDmWorkingSigns`.
|
|
24
26
|
// • A FORWARDED JOB's WAIT (OPE-2536). A job the DM seat forwards to the work session can end waiting
|
|
25
27
|
// on the person: it asked a question in the room (`ask_room`), or it needs their own sign-in. The
|
|
26
28
|
// work session has no `update_status`, so the host sets the session `suspended` at the job's
|
|
@@ -34,7 +36,7 @@
|
|
|
34
36
|
//
|
|
35
37
|
// Every call here is best-effort. A workspace without Slack's agent features answers an error, which
|
|
36
38
|
// is logged and changes nothing else.
|
|
37
|
-
import { cancelAnchoredTurn, resolvePartition, turnAnchorKey, turnChannelKey, } from "@open-everywhere/core/agent/adapter-api.js";
|
|
39
|
+
import { cancelAnchoredTurn, resolvePartition, STATUS_REACTION, swallowRoomOp, turnAnchorKey, turnChannelKey, } from "@open-everywhere/core/agent/adapter-api.js";
|
|
38
40
|
import { bindableForSlackIds, slackForm } from "./slack-events.js";
|
|
39
41
|
/** Slack's event for a press of the session's Stop button. */
|
|
40
42
|
export const AGENT_SESSION_STOPPED_EVENT = "agent_session_stopped";
|
|
@@ -42,6 +44,12 @@ export const AGENT_SESSION_STOPPED_EVENT = "agent_session_stopped";
|
|
|
42
44
|
export const SLACK_SESSION_TITLE_MAX = 60;
|
|
43
45
|
/** The auth attribute the DM door stamps with the session's thread root (slack-front.ts `scheduleDmWake`). */
|
|
44
46
|
const AGENT_SESSION_TS_ATTRIBUTE = "agent_session_ts";
|
|
47
|
+
/**
|
|
48
|
+
* The mark a PINNED bag carries (OPE-2629). A forwarded job's settlement sets the session's state from
|
|
49
|
+
* its pin, not from a turn, so the setter must not touch the turn's working signs for it: a turn of the
|
|
50
|
+
* same session can be running at that moment, and its 👀 must stay until its own park.
|
|
51
|
+
*/
|
|
52
|
+
const SESSION_PIN_ATTRIBUTE = "oe_session_pin";
|
|
45
53
|
// --- the title ------------------------------------------------------------------------------------
|
|
46
54
|
/** Slack's three escaped characters, decoded. */
|
|
47
55
|
function decodeSlackEntities(text) {
|
|
@@ -126,16 +134,92 @@ export const slackSurfaceSessionPin = (attributes) => {
|
|
|
126
134
|
return undefined;
|
|
127
135
|
return {
|
|
128
136
|
id: `${session.channelId}:${session.threadTs}`,
|
|
129
|
-
attributes: { channel_id: session.channelId, channel_type: "im", [AGENT_SESSION_TS_ATTRIBUTE]: session.threadTs },
|
|
137
|
+
attributes: { channel_id: session.channelId, channel_type: "im", [AGENT_SESSION_TS_ATTRIBUTE]: session.threadTs, [SESSION_PIN_ATTRIBUTE]: "1" },
|
|
130
138
|
};
|
|
131
139
|
};
|
|
140
|
+
// --- the working signs ------------------------------------------------------------------------------
|
|
141
|
+
//
|
|
142
|
+
// 🔴 THE DM SEAT'S 👀 AND STATUS LINE LIVE HERE (OPE-2629). A 1:1 DM runs on the `unified` channel, so
|
|
143
|
+
// none of the Slack channel's own handlers reach it: `onInbound` (the 👀) and the `turn.started`
|
|
144
|
+
// renderer (the status line) serve only eve's native mention door. Before this, a person who DMed
|
|
145
|
+
// the app saw neither during the turn. Core's turn-anchor hook calls this setter at every DM turn's
|
|
146
|
+
// start (`processing`) and park (`active` or `suspended`), so the signs ride the same two calls:
|
|
147
|
+
// • 👀 on the session's thread root — the message itself when it is top-level, the same root the
|
|
148
|
+
// mention door's 👀 uses for a threaded one. Added at `processing`, removed at the park.
|
|
149
|
+
// • Slack's assistant status (`assistant.threads.setStatus`) on that root: `Working...` at
|
|
150
|
+
// `processing`, cleared at the park. The flat DM reply does not clear it, so the park must.
|
|
151
|
+
// A forwarded job's settlement (OPE-2536) also calls this setter, from the job's PIN. A turn of the
|
|
152
|
+
// same session can run at that moment, so a pinned bag (`SESSION_PIN_ATTRIBUTE`) moves the session
|
|
153
|
+
// state only and leaves the signs to the turn's own park.
|
|
154
|
+
// A turn that fails for good emits `session.failed` and never parks, so its signs stay: the 👀 until
|
|
155
|
+
// the session's next turn parks, and the status line until Slack expires it.
|
|
156
|
+
/** The status line a DM turn shows while it works — the same words the mention door's `turn.started` shows. */
|
|
157
|
+
export const SLACK_DM_WORKING_STATUS = "Working...";
|
|
158
|
+
/** The 👀 the bot sets while it works (core status/status.ts `STATUS_REACTION.working`). */
|
|
159
|
+
const WORKING_REACTION = STATUS_REACTION.working;
|
|
160
|
+
/**
|
|
161
|
+
* What an operator does when one sign call fails with a setup error (`SIGN_SETUP_ERRORS`). The sign
|
|
162
|
+
* needs a scope or the agent view, and both come from the app manifest, so the fix is the manifest
|
|
163
|
+
* paste. Logged once per method and error, so a workspace without the feature does not log it on
|
|
164
|
+
* every turn.
|
|
165
|
+
*/
|
|
166
|
+
const SIGN_SETUP_FIX = {
|
|
167
|
+
"reactions.add": "the 👀 needs the reactions:write bot scope",
|
|
168
|
+
"reactions.remove": "the 👀 needs the reactions:write bot scope",
|
|
169
|
+
"assistant.threads.setStatus": "the DM status line needs the assistant:write bot scope and the agent view (features.agent_view)",
|
|
170
|
+
};
|
|
171
|
+
/** Slack's answers that mean the app lacks a scope or a feature: a setup gap, not a passing fault. */
|
|
172
|
+
const SIGN_SETUP_ERRORS = new Set(["missing_scope", "feature_disabled", "not_allowed_token_type", "not_allowed", "access_denied"]);
|
|
173
|
+
/** Slack's answers that say the sign is already in the state asked for. */
|
|
174
|
+
const SIGN_NO_OP_ERRORS = new Set(["already_reacted", "no_reaction"]);
|
|
175
|
+
const warnedSignErrors = new Set();
|
|
176
|
+
/** One sign call. Never throws; a failure is logged once per method and error, with the setup fix. */
|
|
177
|
+
async function sendSign(form, method, body) {
|
|
178
|
+
let error;
|
|
179
|
+
try {
|
|
180
|
+
const res = await form(method, body.toString());
|
|
181
|
+
if (res?.ok === true)
|
|
182
|
+
return;
|
|
183
|
+
error = String(res?.error ?? "no answer");
|
|
184
|
+
}
|
|
185
|
+
catch (err) {
|
|
186
|
+
error = String(err);
|
|
187
|
+
}
|
|
188
|
+
if (SIGN_NO_OP_ERRORS.has(error))
|
|
189
|
+
return;
|
|
190
|
+
const key = `${method}:${error}`;
|
|
191
|
+
if (warnedSignErrors.has(key))
|
|
192
|
+
return;
|
|
193
|
+
warnedSignErrors.add(key);
|
|
194
|
+
const fix = SIGN_SETUP_ERRORS.has(error)
|
|
195
|
+
? `${SIGN_SETUP_FIX[method] ?? "this sign needs a manifest scope"}. Fix: paste the app manifest again (App Manifest → Save Changes) and reinstall if Slack asks`
|
|
196
|
+
: "the sign is best-effort and the turn goes on";
|
|
197
|
+
console.warn(`[open-everywhere:slack-session] ${method} failed (${error}) — ${fix}. Logged once per process.`);
|
|
198
|
+
}
|
|
199
|
+
/** Test-only: forget which sign failures were already logged. */
|
|
200
|
+
export function _resetSignWarningsForTest() {
|
|
201
|
+
warnedSignErrors.clear();
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Show or clear the DM turn's working signs on the session's root: the 👀 and the status line. The
|
|
205
|
+
* two calls run in parallel, because the turn waits on this setter. A swallowed room op (the egress
|
|
206
|
+
* spoof) sends neither. Never throws.
|
|
207
|
+
*/
|
|
208
|
+
async function setSlackDmWorkingSigns(session, working, form = slackForm) {
|
|
209
|
+
if (swallowRoomOp(`a slack DM working sign ${working ? "add" : "remove"}`))
|
|
210
|
+
return;
|
|
211
|
+
const reaction = new URLSearchParams({ channel: session.channelId, name: WORKING_REACTION, timestamp: session.threadTs });
|
|
212
|
+
const status = new URLSearchParams({ channel_id: session.channelId, thread_ts: session.threadTs, status: working ? SLACK_DM_WORKING_STATUS : "" });
|
|
213
|
+
await Promise.all([sendSign(form, working ? "reactions.add" : "reactions.remove", reaction), sendSign(form, "assistant.threads.setStatus", status)]);
|
|
214
|
+
}
|
|
132
215
|
/** The Slack setter core's turn-anchor hook calls (registered in register.ts). */
|
|
133
216
|
export function slackSurfaceSessionSetter(form = slackForm) {
|
|
134
217
|
return async (attributes, status) => {
|
|
135
218
|
const session = slackAgentSessionOf(attributes);
|
|
136
219
|
if (!session)
|
|
137
220
|
return undefined;
|
|
138
|
-
|
|
221
|
+
const fromTurn = attributes[SESSION_PIN_ATTRIBUTE] === undefined;
|
|
222
|
+
await Promise.all([setSlackSessionStatus({ ...session, status }, form), ...(fromTurn ? [setSlackDmWorkingSigns(session, status === "processing", form)] : [])]);
|
|
139
223
|
return `${session.channelId}:${session.threadTs}`;
|
|
140
224
|
};
|
|
141
225
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"slack-agent-session.js","sourceRoot":"","sources":["../../../agent/channels/slack-agent-session.ts"],"names":[],"mappings":"AAAA,0GAA0G;AAC1G,EAAE;AACF,kGAAkG;AAClG,sGAAsG;AACtG,yGAAyG;AACzG,0BAA0B;AAC1B,EAAE;AACF,gGAAgG;AAChG,qGAAqG;AACrG,sGAAsG;AACtG,mGAAmG;AACnG,qGAAqG;AACrG,8FAA8F;AAC9F,qGAAqG;AACrG,gGAAgG;AAChG,uGAAuG;AACvG,8CAA8C;AAC9C,uGAAuG;AACvG,2FAA2F;AAC3F,iGAAiG;AACjG,gGAAgG;AAChG,mGAAmG;AACnG,4FAA4F;AAC5F,wGAAwG;AACxG,sGAAsG;AACtG,iGAAiG;AACjG,iGAAiG;AACjG,iFAAiF;AACjF,EAAE;AACF,qGAAqG;AACrG,sGAAsG;AACtG,qGAAqG;AACrG,wFAAwF;AACxF,EAAE;AACF,qGAAqG;AACrG,sCAAsC;AAEtC,OAAO,EACL,kBAAkB,EAClB,gBAAgB,EAChB,aAAa,EACb,cAAc,GAKf,MAAM,4CAA4C,CAAC;AACpD,OAAO,EAAE,mBAAmB,EAAE,SAAS,EAA2B,MAAM,mBAAmB,CAAC;AAE5F,8DAA8D;AAC9D,MAAM,CAAC,MAAM,2BAA2B,GAAG,uBAAuB,CAAC;AAEnE,+FAA+F;AAC/F,MAAM,CAAC,MAAM,uBAAuB,GAAG,EAAE,CAAC;AAK1C,8GAA8G;AAC9G,MAAM,0BAA0B,GAAG,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"slack-agent-session.js","sourceRoot":"","sources":["../../../agent/channels/slack-agent-session.ts"],"names":[],"mappings":"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"}
|