@open-everywhere/provider-slack 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/agent/app-manifest.ts +48 -7
- package/agent/channels/slack-events.ts +12 -0
- package/agent/channels/slack-front.ts +201 -2
- package/agent/channels/slack-home.ts +511 -0
- package/agent/channels/slack-memory-canvas.ts +382 -0
- package/agent/channels/slack-offboarding.ts +89 -0
- package/agent/channels/slack-origin-codec.ts +8 -0
- package/agent/channels/slack-step-label.ts +177 -0
- package/agent/channels/slack.ts +29 -16
- package/agent/docs-generated.ts +1 -1
- package/agent/ops.ts +5 -0
- package/agent/provider-events.ts +1 -1
- package/agent/register.ts +9 -0
- package/agent/setup/page.ts +3 -1
- package/agent/slack-as-you.ts +107 -0
- package/agent/surface-parity.ts +3 -0
- package/dist/agent/app-manifest.d.ts +11 -0
- package/dist/agent/app-manifest.d.ts.map +1 -1
- package/dist/agent/app-manifest.js +39 -7
- package/dist/agent/app-manifest.js.map +1 -1
- package/dist/agent/channels/slack-events.d.ts +8 -0
- package/dist/agent/channels/slack-events.d.ts.map +1 -1
- package/dist/agent/channels/slack-events.js +11 -0
- package/dist/agent/channels/slack-events.js.map +1 -1
- package/dist/agent/channels/slack-front.d.ts +52 -0
- package/dist/agent/channels/slack-front.d.ts.map +1 -1
- package/dist/agent/channels/slack-front.js +134 -2
- package/dist/agent/channels/slack-front.js.map +1 -1
- package/dist/agent/channels/slack-home.d.ts +95 -0
- package/dist/agent/channels/slack-home.d.ts.map +1 -0
- package/dist/agent/channels/slack-home.js +445 -0
- package/dist/agent/channels/slack-home.js.map +1 -0
- package/dist/agent/channels/slack-memory-canvas.d.ts +65 -0
- package/dist/agent/channels/slack-memory-canvas.d.ts.map +1 -0
- package/dist/agent/channels/slack-memory-canvas.js +325 -0
- package/dist/agent/channels/slack-memory-canvas.js.map +1 -0
- package/dist/agent/channels/slack-offboarding.d.ts +31 -0
- package/dist/agent/channels/slack-offboarding.d.ts.map +1 -0
- package/dist/agent/channels/slack-offboarding.js +75 -0
- package/dist/agent/channels/slack-offboarding.js.map +1 -0
- package/dist/agent/channels/slack-origin-codec.d.ts.map +1 -1
- package/dist/agent/channels/slack-origin-codec.js +8 -0
- package/dist/agent/channels/slack-origin-codec.js.map +1 -1
- package/dist/agent/channels/slack-step-label.d.ts +17 -0
- package/dist/agent/channels/slack-step-label.d.ts.map +1 -0
- package/dist/agent/channels/slack-step-label.js +170 -0
- package/dist/agent/channels/slack-step-label.js.map +1 -0
- package/dist/agent/channels/slack.d.ts.map +1 -1
- package/dist/agent/channels/slack.js +29 -16
- package/dist/agent/channels/slack.js.map +1 -1
- package/dist/agent/docs-generated.js +1 -1
- package/dist/agent/docs-generated.js.map +1 -1
- package/dist/agent/ops.d.ts.map +1 -1
- package/dist/agent/ops.js +5 -1
- package/dist/agent/ops.js.map +1 -1
- package/dist/agent/provider-events.js +1 -1
- package/dist/agent/provider-events.js.map +1 -1
- package/dist/agent/register.d.ts.map +1 -1
- package/dist/agent/register.js +9 -1
- package/dist/agent/register.js.map +1 -1
- package/dist/agent/setup/page.d.ts.map +1 -1
- package/dist/agent/setup/page.js +3 -1
- package/dist/agent/setup/page.js.map +1 -1
- package/dist/agent/slack-as-you.d.ts +38 -0
- package/dist/agent/slack-as-you.d.ts.map +1 -0
- package/dist/agent/slack-as-you.js +82 -0
- package/dist/agent/slack-as-you.js.map +1 -0
- package/dist/agent/surface-parity.d.ts.map +1 -1
- package/dist/agent/surface-parity.js +3 -0
- package/dist/agent/surface-parity.js.map +1 -1
- package/package.json +27 -3
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { type HomeBlock, type HomeViewer } from "@open-everywhere/core/agent/adapter-api.js";
|
|
2
|
+
/** Every Home-tab action id starts with this, so the front route can claim it without reading on. */
|
|
3
|
+
export declare const HOME_ACTION_PREFIX: "oe_home:";
|
|
4
|
+
/** The one modal the Home tab opens. Its `callback_id` is how the front route claims a submission. */
|
|
5
|
+
export declare const HOME_MODAL_CALLBACK_ID: "oe_home_action";
|
|
6
|
+
/** Slack refuses a view with more than 100 blocks. The renderer stops below it and says so. */
|
|
7
|
+
export declare const SLACK_HOME_MAX_BLOCKS = 100;
|
|
8
|
+
export interface ParsedHomeOpen {
|
|
9
|
+
readonly teamId?: string;
|
|
10
|
+
readonly enterpriseId?: string;
|
|
11
|
+
readonly userId: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* An `app_home_opened` event for the HOME tab, or undefined. The same event fires when a person
|
|
15
|
+
* opens the Messages tab (`tab: "messages"`), and that one is not ours to answer — publishing the
|
|
16
|
+
* page on every DM open would cost a full build for a tab the person is not looking at.
|
|
17
|
+
*/
|
|
18
|
+
export declare function parseHomeOpened(env: unknown): ParsedHomeOpen | undefined;
|
|
19
|
+
export interface ParsedHomeInteraction {
|
|
20
|
+
readonly userId: string;
|
|
21
|
+
readonly teamId?: string;
|
|
22
|
+
readonly enterpriseId?: string;
|
|
23
|
+
readonly triggerId?: string;
|
|
24
|
+
}
|
|
25
|
+
export interface ParsedHomeClick extends ParsedHomeInteraction {
|
|
26
|
+
/** The core action id (`memory.forget`), with the wire prefix removed. */
|
|
27
|
+
readonly action: string;
|
|
28
|
+
/** The item digest an overflow choice carries. Absent on a button. */
|
|
29
|
+
readonly ref?: string;
|
|
30
|
+
}
|
|
31
|
+
export interface ParsedHomeSubmission extends ParsedHomeInteraction {
|
|
32
|
+
readonly action: string;
|
|
33
|
+
readonly ref?: string;
|
|
34
|
+
/** The Edit dialog's new text. Absent on a confirmation. */
|
|
35
|
+
readonly input?: string;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* A Home-tab `block_actions` click, or undefined. A BUTTON names its action in the action id
|
|
39
|
+
* (`oe_home:memory.export`). An item's OVERFLOW names it in the chosen option's value
|
|
40
|
+
* (`memory.forget|<digest>`), because one overflow carries both of an item's actions.
|
|
41
|
+
*/
|
|
42
|
+
export declare function parseHomeClick(raw: unknown): ParsedHomeClick | undefined;
|
|
43
|
+
/** A submission of the Home tab's own modal, or undefined for any other view. */
|
|
44
|
+
export declare function parseHomeSubmission(raw: unknown): ParsedHomeSubmission | undefined;
|
|
45
|
+
/** Escape the three characters Slack's mrkdwn treats as control. Memory lines are member-authored,
|
|
46
|
+
* and an unescaped `<!channel>` or `<@U…>` in one would ping a room from a private page. */
|
|
47
|
+
export declare function escapeSlackText(text: string): string;
|
|
48
|
+
type SlackBlock = Record<string, unknown>;
|
|
49
|
+
/**
|
|
50
|
+
* The whole Home view as Slack Block Kit. PURE.
|
|
51
|
+
*
|
|
52
|
+
* 🔴 THE 100-BLOCK CAP IS SLACK'S, AND A VIEW OVER IT IS REFUSED WHOLE. A refused publish leaves the
|
|
53
|
+
* person looking at a stale page with no word of why, so the renderer stops two blocks short of the
|
|
54
|
+
* cap and closes with a line that says the page was cut and Export carries the rest.
|
|
55
|
+
*/
|
|
56
|
+
export declare function renderHomeView(sections: readonly {
|
|
57
|
+
readonly blocks: readonly HomeBlock[];
|
|
58
|
+
}[], notice?: string): {
|
|
59
|
+
type: "home";
|
|
60
|
+
blocks: SlackBlock[];
|
|
61
|
+
};
|
|
62
|
+
/** The Slack calls the viewer needs. Injected so the gate is tested without Slack. */
|
|
63
|
+
export interface SlackHomeDeps {
|
|
64
|
+
/** One Slack Web API call; undefined on any failure (fail-soft, like `slackForm`). */
|
|
65
|
+
readonly call: (method: string, body: Record<string, unknown>) => Promise<Record<string, unknown> | undefined>;
|
|
66
|
+
/** Is this person a full member of the workspace? undefined when the lookup failed. */
|
|
67
|
+
readonly authorIsMember: (userId: string, teamId: string | undefined) => Promise<boolean | undefined>;
|
|
68
|
+
/** Is this channel confirmed internal? false on any doubt. */
|
|
69
|
+
readonly channelInternal: (channelId: string) => Promise<boolean>;
|
|
70
|
+
}
|
|
71
|
+
/** The live deps: every call through `slackForm` (the one Web API seam, fail-soft), the shared member
|
|
72
|
+
* probe, and the shared fail-closed channel gate. */
|
|
73
|
+
export declare function liveSlackHomeDeps(): SlackHomeDeps;
|
|
74
|
+
/**
|
|
75
|
+
* Build the VERIFIED viewer for one Slack person. Every input is from the signed envelope.
|
|
76
|
+
*
|
|
77
|
+
* • the personal partition: `conversations.open` with the person names THEIR DM with the app,
|
|
78
|
+
* the same `D…` their DM wakes bind to. On failure the personal section says it could not load.
|
|
79
|
+
* • trust: `users.info` through the shared member probe. Unresolvable ⇒ external (fail-closed).
|
|
80
|
+
* • domains (internal viewers only): the channels Slack lists for the person, mapped to a domain
|
|
81
|
+
* through the SAME router a wake uses, kept only where Slack confirms the channel internal.
|
|
82
|
+
*/
|
|
83
|
+
export declare function buildSlackHomeViewer(ids: ParsedHomeInteraction, deps: SlackHomeDeps): Promise<HomeViewer | undefined>;
|
|
84
|
+
/** `app_home_opened` → build the viewer, publish the page. */
|
|
85
|
+
export declare function handleHomeOpened(ev: ParsedHomeOpen, deps: SlackHomeDeps): Promise<boolean>;
|
|
86
|
+
/**
|
|
87
|
+
* A Home-tab click. A button runs its action and republishes. An overflow choice opens the action's
|
|
88
|
+
* modal — the item text it quotes is read from the VIEWER's own page model, never from the payload,
|
|
89
|
+
* so a forged digest that names no line of theirs opens nothing.
|
|
90
|
+
*/
|
|
91
|
+
export declare function handleHomeClick(click: ParsedHomeClick, deps: SlackHomeDeps): Promise<boolean>;
|
|
92
|
+
/** A Home modal's submission → run the action, republish with its notice. */
|
|
93
|
+
export declare function handleHomeSubmission(sub: ParsedHomeSubmission, deps: SlackHomeDeps): Promise<boolean>;
|
|
94
|
+
export {};
|
|
95
|
+
//# sourceMappingURL=slack-home.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slack-home.d.ts","sourceRoot":"","sources":["../../../agent/channels/slack-home.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,445 @@
|
|
|
1
|
+
// THE SLACK APP HOME TAB (OPE-2451) — the person's own page: what the agent remembers about them,
|
|
2
|
+
// what their domains know, and (OPE-2456, later) their rules. Core owns the page model
|
|
3
|
+
// (`home/home-view.ts`); this module is the Slack half: it parses the three Slack deliveries the
|
|
4
|
+
// page needs, builds the VERIFIED viewer, renders Block Kit, and drives the dialogs.
|
|
5
|
+
//
|
|
6
|
+
// THE THREE DELIVERIES, all classified by the front route (slack-front.ts) and never forwarded:
|
|
7
|
+
// • `app_home_opened` (Events API, `tab: "home"`) → build the viewer, `views.publish` the page.
|
|
8
|
+
// • a `block_actions` whose action id starts `oe_home:` → a button (Export, Refresh) runs at once;
|
|
9
|
+
// an item's overflow choice (Edit, Forget) opens a modal.
|
|
10
|
+
// • a `view_submission` whose `callback_id` is {@link HOME_MODAL_CALLBACK_ID} → run the action the
|
|
11
|
+
// modal named, then publish the page again with a one-line notice.
|
|
12
|
+
//
|
|
13
|
+
// 🔴 THE VIEWER COMES FROM THE SIGNED ENVELOPE, NEVER FROM A FIELD WE WROTE. The page is private to
|
|
14
|
+
// one person because Slack shows each person their own Home tab, and that only holds if every read
|
|
15
|
+
// and every write is keyed to the person Slack says is looking. So the viewer is built from the
|
|
16
|
+
// event's `user` / the interaction's `user.id` and `team`, every time. A modal's `private_metadata`
|
|
17
|
+
// carries ONLY the action id and the line digest — never a user, a channel, a partition or a silo —
|
|
18
|
+
// and core looks the digest up inside the viewer's own memory, so a forged digest names nothing the
|
|
19
|
+
// clicker does not already own.
|
|
20
|
+
//
|
|
21
|
+
// 🔴 THE TRUST GATE, APPLIED TO A PERSON. A domain's memory is internal knowledge. The page lists a
|
|
22
|
+
// domain only when the viewer is a full member of the workspace (`users.info`, fail-closed) AND sits
|
|
23
|
+
// in one of the domain's channels that Slack confirms is internal (`conversations.info`, fail-closed).
|
|
24
|
+
// A guest, or a person whose only route into a domain is a Slack Connect channel, sees their own
|
|
25
|
+
// memory and no domain's.
|
|
26
|
+
import { appendAudit, bindFrom, buildHomeView, domainForChannel, getConfig, hasHomeAction, homeDomainsFor, resolvePartition, runHomeAction, SLACK_HOME_WIRE, } from "@open-everywhere/core/agent/adapter-api.js";
|
|
27
|
+
import { slackInteractivityTenantIds } from "./slack-tenant-fence.js";
|
|
28
|
+
import { confirmChannelInternal, slackAuthorIsMember, slackForm } from "./slack-events.js";
|
|
29
|
+
import { encodeSlackForm } from "./slack-web-api.js";
|
|
30
|
+
// ── WIRE CONSTANTS ──────────────────────────────────────────────────────────────────────────────
|
|
31
|
+
/** Every Home-tab action id starts with this, so the front route can claim it without reading on. */
|
|
32
|
+
export const HOME_ACTION_PREFIX = SLACK_HOME_WIRE.actionPrefix;
|
|
33
|
+
/** The one modal the Home tab opens. Its `callback_id` is how the front route claims a submission. */
|
|
34
|
+
export const HOME_MODAL_CALLBACK_ID = SLACK_HOME_WIRE.modalCallbackId;
|
|
35
|
+
/** The block and element ids of the modal's text input (the Edit dialog). */
|
|
36
|
+
const EDIT_BLOCK_ID = "oe_home_edit";
|
|
37
|
+
const EDIT_ELEMENT_ID = "text";
|
|
38
|
+
/** Slack refuses a view with more than 100 blocks. The renderer stops below it and says so. */
|
|
39
|
+
export const SLACK_HOME_MAX_BLOCKS = 100;
|
|
40
|
+
/** A section block's text is capped at 3000 characters by Slack. An item is cut well below that. */
|
|
41
|
+
const ITEM_TEXT_MAX = 600;
|
|
42
|
+
/** How many of the viewer's channels the domain lookup pages through (200 per page). */
|
|
43
|
+
const MEMBERSHIP_PAGES_MAX = 5;
|
|
44
|
+
/**
|
|
45
|
+
* An `app_home_opened` event for the HOME tab, or undefined. The same event fires when a person
|
|
46
|
+
* opens the Messages tab (`tab: "messages"`), and that one is not ours to answer — publishing the
|
|
47
|
+
* page on every DM open would cost a full build for a tab the person is not looking at.
|
|
48
|
+
*/
|
|
49
|
+
export function parseHomeOpened(env) {
|
|
50
|
+
const e = asObject(env);
|
|
51
|
+
const ev = asObject(e?.event);
|
|
52
|
+
if (e?.type !== "event_callback" || ev?.type !== "app_home_opened" || ev.tab !== "home")
|
|
53
|
+
return undefined;
|
|
54
|
+
const userId = str(ev.user);
|
|
55
|
+
if (!userId)
|
|
56
|
+
return undefined;
|
|
57
|
+
const teamId = str(e.team_id);
|
|
58
|
+
const enterpriseId = str(e.enterprise_id);
|
|
59
|
+
return { userId, ...(teamId ? { teamId } : {}), ...(enterpriseId ? { enterpriseId } : {}) };
|
|
60
|
+
}
|
|
61
|
+
function interactionIds(p) {
|
|
62
|
+
const userId = str(asObject(p.user)?.id);
|
|
63
|
+
if (!userId)
|
|
64
|
+
return undefined;
|
|
65
|
+
const { teamId, enterpriseId } = slackInteractivityTenantIds(p);
|
|
66
|
+
const triggerId = str(p.trigger_id);
|
|
67
|
+
return { userId, ...(teamId ? { teamId } : {}), ...(enterpriseId ? { enterpriseId } : {}), ...(triggerId ? { triggerId } : {}) };
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* A Home-tab `block_actions` click, or undefined. A BUTTON names its action in the action id
|
|
71
|
+
* (`oe_home:memory.export`). An item's OVERFLOW names it in the chosen option's value
|
|
72
|
+
* (`memory.forget|<digest>`), because one overflow carries both of an item's actions.
|
|
73
|
+
*/
|
|
74
|
+
export function parseHomeClick(raw) {
|
|
75
|
+
const p = asObject(raw);
|
|
76
|
+
if (p?.type !== "block_actions" || !Array.isArray(p.actions))
|
|
77
|
+
return undefined;
|
|
78
|
+
const ids = interactionIds(p);
|
|
79
|
+
if (!ids)
|
|
80
|
+
return undefined;
|
|
81
|
+
for (const a of p.actions) {
|
|
82
|
+
const act = asObject(a);
|
|
83
|
+
const actionId = str(act?.action_id);
|
|
84
|
+
if (!actionId?.startsWith(HOME_ACTION_PREFIX))
|
|
85
|
+
continue;
|
|
86
|
+
if (act?.type === "overflow") {
|
|
87
|
+
const value = str(asObject(act.selected_option)?.value);
|
|
88
|
+
const split = value ? splitActionValue(value) : undefined;
|
|
89
|
+
if (split)
|
|
90
|
+
return { ...ids, ...split };
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
const action = actionId.slice(HOME_ACTION_PREFIX.length);
|
|
94
|
+
if (action)
|
|
95
|
+
return { ...ids, action };
|
|
96
|
+
}
|
|
97
|
+
return undefined;
|
|
98
|
+
}
|
|
99
|
+
/** A submission of the Home tab's own modal, or undefined for any other view. */
|
|
100
|
+
export function parseHomeSubmission(raw) {
|
|
101
|
+
const p = asObject(raw);
|
|
102
|
+
if (p?.type !== "view_submission")
|
|
103
|
+
return undefined;
|
|
104
|
+
const view = asObject(p.view);
|
|
105
|
+
if (view?.callback_id !== HOME_MODAL_CALLBACK_ID)
|
|
106
|
+
return undefined;
|
|
107
|
+
const ids = interactionIds(p);
|
|
108
|
+
if (!ids)
|
|
109
|
+
return undefined;
|
|
110
|
+
const meta = str(view.private_metadata);
|
|
111
|
+
const split = meta ? splitActionValue(meta) : undefined;
|
|
112
|
+
if (!split)
|
|
113
|
+
return undefined;
|
|
114
|
+
const values = asObject(asObject(view.state)?.values);
|
|
115
|
+
const input = str(asObject(asObject(values?.[EDIT_BLOCK_ID])?.[EDIT_ELEMENT_ID])?.value);
|
|
116
|
+
return { ...ids, ...split, ...(input !== undefined ? { input } : {}) };
|
|
117
|
+
}
|
|
118
|
+
/** `action|ref` → its parts. The ref, when present, must look like a line digest. PURE. */
|
|
119
|
+
function splitActionValue(value) {
|
|
120
|
+
const bar = value.indexOf("|");
|
|
121
|
+
const action = bar === -1 ? value : value.slice(0, bar);
|
|
122
|
+
const ref = bar === -1 ? undefined : value.slice(bar + 1);
|
|
123
|
+
if (!/^[a-z][a-z0-9._-]{0,63}$/.test(action))
|
|
124
|
+
return undefined;
|
|
125
|
+
if (ref !== undefined && !/^[0-9a-f]{64}$/.test(ref))
|
|
126
|
+
return undefined;
|
|
127
|
+
return { action, ...(ref !== undefined ? { ref } : {}) };
|
|
128
|
+
}
|
|
129
|
+
// ── RENDERING (pure) ────────────────────────────────────────────────────────────────────────────
|
|
130
|
+
/** Escape the three characters Slack's mrkdwn treats as control. Memory lines are member-authored,
|
|
131
|
+
* and an unescaped `<!channel>` or `<@U…>` in one would ping a room from a private page. */
|
|
132
|
+
export function escapeSlackText(text) {
|
|
133
|
+
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
134
|
+
}
|
|
135
|
+
function clip(text, max) {
|
|
136
|
+
return text.length <= max ? text : text.slice(0, max - 1) + "…";
|
|
137
|
+
}
|
|
138
|
+
function plain(text) {
|
|
139
|
+
return { type: "plain_text", text: clip(text, 150), emoji: true };
|
|
140
|
+
}
|
|
141
|
+
/** One neutral block → Slack blocks. */
|
|
142
|
+
function renderBlock(b) {
|
|
143
|
+
switch (b.kind) {
|
|
144
|
+
case "heading":
|
|
145
|
+
return [{ type: "header", text: plain(b.text) }];
|
|
146
|
+
case "text":
|
|
147
|
+
return [{ type: "section", text: { type: "mrkdwn", text: escapeSlackText(clip(b.text, 2900)) } }];
|
|
148
|
+
case "note":
|
|
149
|
+
return [{ type: "context", elements: [{ type: "mrkdwn", text: escapeSlackText(clip(b.text, 2900)) }] }];
|
|
150
|
+
case "divider":
|
|
151
|
+
return [{ type: "divider" }];
|
|
152
|
+
case "buttons":
|
|
153
|
+
return [
|
|
154
|
+
{
|
|
155
|
+
type: "actions",
|
|
156
|
+
elements: b.buttons.map((btn) => ({ type: "button", action_id: `${HOME_ACTION_PREFIX}${btn.id}`, text: plain(btn.label) })),
|
|
157
|
+
},
|
|
158
|
+
];
|
|
159
|
+
case "room":
|
|
160
|
+
return [{ type: "context", elements: [{ type: "mrkdwn", text: `${escapeSlackText(b.text)} <#${b.room.nativeId}>` }] }];
|
|
161
|
+
case "item": {
|
|
162
|
+
const label = b.label ? `*${escapeSlackText(clip(b.label, 80))}* · ` : "";
|
|
163
|
+
const section = { type: "section", text: { type: "mrkdwn", text: label + escapeSlackText(clip(b.text, ITEM_TEXT_MAX)) } };
|
|
164
|
+
if (b.ref && b.actions.length > 0) {
|
|
165
|
+
section.accessory = {
|
|
166
|
+
type: "overflow",
|
|
167
|
+
action_id: `${HOME_ACTION_PREFIX}item`,
|
|
168
|
+
options: b.actions.map((a) => ({ text: plain(a.label), value: `${a.id}|${b.ref}` })),
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
return [section];
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* The whole Home view as Slack Block Kit. PURE.
|
|
177
|
+
*
|
|
178
|
+
* 🔴 THE 100-BLOCK CAP IS SLACK'S, AND A VIEW OVER IT IS REFUSED WHOLE. A refused publish leaves the
|
|
179
|
+
* person looking at a stale page with no word of why, so the renderer stops two blocks short of the
|
|
180
|
+
* cap and closes with a line that says the page was cut and Export carries the rest.
|
|
181
|
+
*/
|
|
182
|
+
export function renderHomeView(sections, notice) {
|
|
183
|
+
const blocks = [];
|
|
184
|
+
if (notice)
|
|
185
|
+
blocks.push({ type: "section", text: { type: "mrkdwn", text: `:information_source: ${escapeSlackText(clip(notice, 2900))}` } });
|
|
186
|
+
const room = SLACK_HOME_MAX_BLOCKS - 1;
|
|
187
|
+
let cut = false;
|
|
188
|
+
outer: for (const s of sections) {
|
|
189
|
+
for (const b of s.blocks) {
|
|
190
|
+
const rendered = renderBlock(b);
|
|
191
|
+
if (blocks.length + rendered.length > room) {
|
|
192
|
+
cut = true;
|
|
193
|
+
break outer;
|
|
194
|
+
}
|
|
195
|
+
blocks.push(...rendered);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
if (cut)
|
|
199
|
+
blocks.push({ type: "context", elements: [{ type: "mrkdwn", text: "This page is full, so some lines are not shown. Export gives you everything in your memory." }] });
|
|
200
|
+
return { type: "home", blocks };
|
|
201
|
+
}
|
|
202
|
+
/** The modal an item's overflow choice opens. `edit` asks for the new text, `confirm` asks for a yes. */
|
|
203
|
+
function homeActionModal(input) {
|
|
204
|
+
const quoted = escapeSlackText(clip(input.itemText, 2800));
|
|
205
|
+
return {
|
|
206
|
+
type: "modal",
|
|
207
|
+
callback_id: HOME_MODAL_CALLBACK_ID,
|
|
208
|
+
private_metadata: `${input.action}|${input.ref}`,
|
|
209
|
+
title: plain(clip(input.title, 24)),
|
|
210
|
+
submit: plain(clip(input.yes, 24)),
|
|
211
|
+
close: plain("Cancel"),
|
|
212
|
+
blocks: input.kind === "edit"
|
|
213
|
+
? [
|
|
214
|
+
{
|
|
215
|
+
type: "input",
|
|
216
|
+
block_id: EDIT_BLOCK_ID,
|
|
217
|
+
label: plain("The fact, as I should remember it"),
|
|
218
|
+
element: { type: "plain_text_input", action_id: EDIT_ELEMENT_ID, initial_value: clip(input.itemText, 1000), max_length: 1000 },
|
|
219
|
+
},
|
|
220
|
+
{ type: "context", elements: [{ type: "mrkdwn", text: "The audit log records that this line changed, by fingerprint — never the words." }] },
|
|
221
|
+
]
|
|
222
|
+
: [
|
|
223
|
+
{ type: "section", text: { type: "mrkdwn", text: `> ${quoted}` } },
|
|
224
|
+
{ type: "context", elements: [{ type: "mrkdwn", text: "I remove this line from your memory everywhere it is stored. You cannot undo it." }] },
|
|
225
|
+
],
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
/** The live deps: every call through `slackForm` (the one Web API seam, fail-soft), the shared member
|
|
229
|
+
* probe, and the shared fail-closed channel gate. */
|
|
230
|
+
export function liveSlackHomeDeps() {
|
|
231
|
+
return {
|
|
232
|
+
call: (method, body) => slackForm(method, encodeSlackForm(body)),
|
|
233
|
+
authorIsMember: slackAuthorIsMember,
|
|
234
|
+
channelInternal: confirmChannelInternal,
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
/** The BindableContext a person's own DM resolves to — the SAME shape a DM wake binds with, so the
|
|
238
|
+
* partition (org, workspace, surface) is the one the DM's memory was written under. */
|
|
239
|
+
function dmBindable(ids) {
|
|
240
|
+
return {
|
|
241
|
+
channel: { kind: "slack" },
|
|
242
|
+
session: {
|
|
243
|
+
auth: {
|
|
244
|
+
current: {
|
|
245
|
+
attributes: { team_id: ids.teamId ?? "", channel_id: ids.channelId, channel_type: "im" },
|
|
246
|
+
principalId: ids.userId,
|
|
247
|
+
principalType: "slack-user",
|
|
248
|
+
},
|
|
249
|
+
},
|
|
250
|
+
},
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Build the VERIFIED viewer for one Slack person. Every input is from the signed envelope.
|
|
255
|
+
*
|
|
256
|
+
* • the personal partition: `conversations.open` with the person names THEIR DM with the app,
|
|
257
|
+
* the same `D…` their DM wakes bind to. On failure the personal section says it could not load.
|
|
258
|
+
* • trust: `users.info` through the shared member probe. Unresolvable ⇒ external (fail-closed).
|
|
259
|
+
* • domains (internal viewers only): the channels Slack lists for the person, mapped to a domain
|
|
260
|
+
* through the SAME router a wake uses, kept only where Slack confirms the channel internal.
|
|
261
|
+
*/
|
|
262
|
+
export async function buildSlackHomeViewer(ids, deps) {
|
|
263
|
+
const opened = await deps.call("conversations.open", { users: ids.userId });
|
|
264
|
+
const dmId = opened?.ok === true ? str(asObject(opened.channel)?.id) : undefined;
|
|
265
|
+
const anchor = dmBindable({ ...ids, channelId: dmId ?? "home" });
|
|
266
|
+
const binding = bindFrom(anchor);
|
|
267
|
+
if (!binding.principalId)
|
|
268
|
+
return undefined;
|
|
269
|
+
const partition = resolvePartition(anchor);
|
|
270
|
+
const member = await deps.authorIsMember(ids.userId, ids.teamId);
|
|
271
|
+
const trust = member === true ? "internal" : "external";
|
|
272
|
+
const memberships = trust === "internal" ? await slackDomainMemberships(ids.userId, partition.workspaceKey, deps) : [];
|
|
273
|
+
return {
|
|
274
|
+
org: partition.org,
|
|
275
|
+
platform: partition.platform,
|
|
276
|
+
workspace: partition.workspace,
|
|
277
|
+
principalId: binding.principalId,
|
|
278
|
+
...(dmId ? { personal: partition } : {}),
|
|
279
|
+
trust,
|
|
280
|
+
domains: homeDomainsFor(trust, memberships),
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
/** The domains a person belongs to through an internal channel. Fail-soft: a failed page ends the walk. */
|
|
284
|
+
async function slackDomainMemberships(userId, workspaceKey, deps) {
|
|
285
|
+
const domains = getConfig().loadDomains();
|
|
286
|
+
const byDomain = new Map();
|
|
287
|
+
let cursor;
|
|
288
|
+
for (let page = 0; page < MEMBERSHIP_PAGES_MAX; page++) {
|
|
289
|
+
const res = await deps.call("users.conversations", {
|
|
290
|
+
user: userId,
|
|
291
|
+
types: "public_channel,private_channel",
|
|
292
|
+
exclude_archived: true,
|
|
293
|
+
limit: 200,
|
|
294
|
+
...(cursor ? { cursor } : {}),
|
|
295
|
+
});
|
|
296
|
+
if (res?.ok !== true || !Array.isArray(res.channels))
|
|
297
|
+
break;
|
|
298
|
+
for (const c of res.channels) {
|
|
299
|
+
const channelId = str(asObject(c)?.id);
|
|
300
|
+
if (!channelId)
|
|
301
|
+
continue;
|
|
302
|
+
const surfaceKey = `${workspaceKey}/${channelId}`;
|
|
303
|
+
const domain = domainForChannel(surfaceKey, domains);
|
|
304
|
+
if (!domain || domain.memory === undefined)
|
|
305
|
+
continue;
|
|
306
|
+
if (!(await deps.channelInternal(channelId)))
|
|
307
|
+
continue;
|
|
308
|
+
const existing = byDomain.get(domain.name);
|
|
309
|
+
const room = { surfaceKey, nativeId: channelId };
|
|
310
|
+
byDomain.set(domain.name, existing ? { domain, rooms: [...existing.rooms, room] } : { domain, rooms: [room] });
|
|
311
|
+
}
|
|
312
|
+
cursor = str(asObject(res.response_metadata)?.next_cursor);
|
|
313
|
+
if (!cursor)
|
|
314
|
+
break;
|
|
315
|
+
}
|
|
316
|
+
return [...byDomain.values()];
|
|
317
|
+
}
|
|
318
|
+
// ── THE THREE HANDLERS ──────────────────────────────────────────────────────────────────────────
|
|
319
|
+
/** The audit action every publish attempt writes. Declared in core's registry (operational). */
|
|
320
|
+
const HOME_PUBLISH_ACTION = "home.publish";
|
|
321
|
+
/**
|
|
322
|
+
* Publish the page for one viewer, with an optional notice at the top, and write one `home.publish`
|
|
323
|
+
* row. The row is how a reader outside Slack (the e2e board, an operator) learns the page reached a
|
|
324
|
+
* person: Slack offers no API to read another person's Home tab back. It carries counts and Slack's
|
|
325
|
+
* error code only — never a line of the page.
|
|
326
|
+
*/
|
|
327
|
+
async function publishHome(userId, viewer, deps, notice) {
|
|
328
|
+
const view = renderHomeView(await buildHomeView(viewer), notice);
|
|
329
|
+
const res = await deps.call("views.publish", { user_id: userId, view });
|
|
330
|
+
const ok = res?.ok === true;
|
|
331
|
+
if (!ok)
|
|
332
|
+
console.log(`[open-everywhere:slack-home] views.publish failed: ${String(res?.error ?? "no response")}`);
|
|
333
|
+
await appendAudit({
|
|
334
|
+
org: viewer.org,
|
|
335
|
+
platform: viewer.platform,
|
|
336
|
+
workspace: viewer.workspace,
|
|
337
|
+
surface: viewer.personal?.surface ?? "home",
|
|
338
|
+
principalId: viewer.principalId,
|
|
339
|
+
action: HOME_PUBLISH_ACTION,
|
|
340
|
+
detail: {
|
|
341
|
+
ok,
|
|
342
|
+
blocks: view.blocks.length,
|
|
343
|
+
trust: viewer.trust,
|
|
344
|
+
domains: viewer.domains.length,
|
|
345
|
+
notice: notice !== undefined,
|
|
346
|
+
...(ok ? {} : { error: typeof res?.error === "string" ? res.error : "no response" }),
|
|
347
|
+
},
|
|
348
|
+
}).catch(() => undefined);
|
|
349
|
+
return ok;
|
|
350
|
+
}
|
|
351
|
+
/** `app_home_opened` → build the viewer, publish the page. */
|
|
352
|
+
export async function handleHomeOpened(ev, deps) {
|
|
353
|
+
const viewer = await buildSlackHomeViewer(ev, deps);
|
|
354
|
+
if (!viewer)
|
|
355
|
+
return false;
|
|
356
|
+
return publishHome(ev.userId, viewer, deps);
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
* A Home-tab click. A button runs its action and republishes. An overflow choice opens the action's
|
|
360
|
+
* modal — the item text it quotes is read from the VIEWER's own page model, never from the payload,
|
|
361
|
+
* so a forged digest that names no line of theirs opens nothing.
|
|
362
|
+
*/
|
|
363
|
+
export async function handleHomeClick(click, deps) {
|
|
364
|
+
if (!hasHomeAction(click.action))
|
|
365
|
+
return false;
|
|
366
|
+
const viewer = await buildSlackHomeViewer(click, deps);
|
|
367
|
+
if (!viewer)
|
|
368
|
+
return false;
|
|
369
|
+
if (click.ref === undefined) {
|
|
370
|
+
const result = await runHomeAction(viewer, click.action);
|
|
371
|
+
if (result.file)
|
|
372
|
+
await deliverFile(click.userId, result.file, result.notice, deps);
|
|
373
|
+
const notice = result.file ? `${result.notice} I sent it to our direct messages.` : result.notice;
|
|
374
|
+
return publishHome(click.userId, viewer, deps, notice || undefined);
|
|
375
|
+
}
|
|
376
|
+
const located = await locateItem(viewer, click.action, click.ref);
|
|
377
|
+
if (!located || !click.triggerId) {
|
|
378
|
+
return publishHome(click.userId, viewer, deps, "That line is not on your page any more.");
|
|
379
|
+
}
|
|
380
|
+
const res = await deps.call("views.open", {
|
|
381
|
+
trigger_id: click.triggerId,
|
|
382
|
+
view: homeActionModal({
|
|
383
|
+
action: click.action,
|
|
384
|
+
ref: click.ref,
|
|
385
|
+
kind: located.action.kind,
|
|
386
|
+
itemText: located.text,
|
|
387
|
+
title: located.action.confirm?.title ?? `${located.action.label}`,
|
|
388
|
+
yes: located.action.confirm?.yes ?? "Save",
|
|
389
|
+
}),
|
|
390
|
+
});
|
|
391
|
+
return res?.ok === true;
|
|
392
|
+
}
|
|
393
|
+
/** Find the item a digest names on the VIEWER's own page, and the action it offers. */
|
|
394
|
+
async function locateItem(viewer, actionId, ref) {
|
|
395
|
+
for (const section of await buildHomeView(viewer)) {
|
|
396
|
+
for (const b of section.blocks) {
|
|
397
|
+
if (b.kind !== "item" || b.ref !== ref)
|
|
398
|
+
continue;
|
|
399
|
+
const action = b.actions.find((a) => a.id === actionId);
|
|
400
|
+
if (action)
|
|
401
|
+
return { text: b.text, action };
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
return undefined;
|
|
405
|
+
}
|
|
406
|
+
/** A Home modal's submission → run the action, republish with its notice. */
|
|
407
|
+
export async function handleHomeSubmission(sub, deps) {
|
|
408
|
+
if (!hasHomeAction(sub.action))
|
|
409
|
+
return false;
|
|
410
|
+
const viewer = await buildSlackHomeViewer(sub, deps);
|
|
411
|
+
if (!viewer)
|
|
412
|
+
return false;
|
|
413
|
+
const result = await runHomeAction(viewer, sub.action, sub.ref, sub.input);
|
|
414
|
+
return publishHome(sub.userId, viewer, deps, result.notice || undefined);
|
|
415
|
+
}
|
|
416
|
+
/** Hand a document to the person: upload it into their DM with the app (Slack's external upload). */
|
|
417
|
+
async function deliverFile(userId, file, comment, deps) {
|
|
418
|
+
const opened = await deps.call("conversations.open", { users: userId });
|
|
419
|
+
const channelId = opened?.ok === true ? str(asObject(opened.channel)?.id) : undefined;
|
|
420
|
+
if (!channelId)
|
|
421
|
+
return false;
|
|
422
|
+
const bytes = new TextEncoder().encode(file.content);
|
|
423
|
+
const url = await deps.call("files.getUploadURLExternal", { filename: file.filename, length: bytes.byteLength });
|
|
424
|
+
const uploadUrl = url?.ok === true ? str(url.upload_url) : undefined;
|
|
425
|
+
const fileId = url?.ok === true ? str(url.file_id) : undefined;
|
|
426
|
+
if (!uploadUrl || !fileId)
|
|
427
|
+
return false;
|
|
428
|
+
const put = await fetch(uploadUrl, { method: "POST", headers: { "content-type": "text/markdown" }, body: bytes }).catch(() => undefined);
|
|
429
|
+
if (!put?.ok)
|
|
430
|
+
return false;
|
|
431
|
+
const done = await deps.call("files.completeUploadExternal", {
|
|
432
|
+
files: [{ id: fileId, title: file.title }],
|
|
433
|
+
channel_id: channelId,
|
|
434
|
+
initial_comment: comment,
|
|
435
|
+
});
|
|
436
|
+
return done?.ok === true;
|
|
437
|
+
}
|
|
438
|
+
// ── helpers ─────────────────────────────────────────────────────────────────────────────────────
|
|
439
|
+
function asObject(v) {
|
|
440
|
+
return typeof v === "object" && v !== null && !Array.isArray(v) ? v : undefined;
|
|
441
|
+
}
|
|
442
|
+
function str(v) {
|
|
443
|
+
return typeof v === "string" && v.length > 0 ? v : undefined;
|
|
444
|
+
}
|
|
445
|
+
//# sourceMappingURL=slack-home.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slack-home.js","sourceRoot":"","sources":["../../../agent/channels/slack-home.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { type MemoryWriteEvent } from "@open-everywhere/core/agent/adapter-api.js";
|
|
2
|
+
export type CanvasOutcome = "created" | "updated" | "unchanged" | "skipped-external" | "skipped-guest" | "skipped-foreign" | "skipped-disabled" | "cleared-external" | "cleared-guest" | "cleared-disabled" | "clear-failed" | "failed";
|
|
3
|
+
export interface MemoryCanvasDeps {
|
|
4
|
+
/** One Slack Web API call; undefined on any failure. */
|
|
5
|
+
readonly call: (method: string, body: Record<string, unknown>) => Promise<Record<string, unknown> | undefined>;
|
|
6
|
+
/** Is this channel confirmed internal? false on any doubt. 🔴 The live dep reads it FRESH (it drops
|
|
7
|
+
* the cached verdict first): a cached `internal` from before a share must never clear the gate. */
|
|
8
|
+
readonly channelInternal: (channelId: string) => Promise<boolean>;
|
|
9
|
+
/** Is this person a full member of the workspace? undefined when the lookup failed. The shared
|
|
10
|
+
* `users.info` probe (slack-events.ts `slackAuthorIsMember`). */
|
|
11
|
+
readonly authorIsMember: (userId: string, teamId: string | undefined) => Promise<boolean | undefined>;
|
|
12
|
+
readonly now?: () => Date;
|
|
13
|
+
}
|
|
14
|
+
/** The live deps: every call through `slackForm` (the one Web API seam, fail-soft), the room verdict
|
|
15
|
+
* read FRESH, and the shared per-person membership probe. */
|
|
16
|
+
export declare function liveMemoryCanvasDeps(): MemoryCanvasDeps;
|
|
17
|
+
/**
|
|
18
|
+
* Make memory text safe for a canvas a whole channel reads. A remembered line is member-authored
|
|
19
|
+
* text, and canvas markdown turns `` into a mention and `<!channel>`-shaped text into a ping
|
|
20
|
+
* on some clients. A memory view must never notify anyone, so both forms are defused. PURE.
|
|
21
|
+
*/
|
|
22
|
+
export declare function defuseCanvasText(markdown: string): string;
|
|
23
|
+
/**
|
|
24
|
+
* Write one channel's canvas — or, with `markdown: null`, only RECHECK it. Never throws.
|
|
25
|
+
*
|
|
26
|
+
* 🔴 THE GATE RUNS BEFORE EVERY DECISION, AND A FAILED GATE TAKES THE APP'S OWN CANVAS DOWN (OPE-2451,
|
|
27
|
+
* the brain's security review). A canvas the app wrote while the channel passed outlives the room's
|
|
28
|
+
* trust otherwise: once the channel is shared, or a guest joins, a refresh that merely SKIPS leaves
|
|
29
|
+
* every line of the domain's memory on screen for the new audience. So on either refusal the app
|
|
30
|
+
* replaces its own canvas with {@link withdrawnCanvasMarkdown} ({@link withdrawIfOurs}). A canvas a
|
|
31
|
+
* PERSON made is never touched, in either direction.
|
|
32
|
+
*
|
|
33
|
+
* `null` markdown is the recheck the trust EVENTS run: it takes the canvas down on a failed gate and
|
|
34
|
+
* writes nothing on a passing one (`unchanged`). `joinedUserId` is the person a join event named: they
|
|
35
|
+
* are probed DIRECTLY, because Slack's member list may not carry them yet when the event arrives.
|
|
36
|
+
*/
|
|
37
|
+
export declare function publishChannelCanvas(channelId: string, markdown: string | null, botUserId: string | undefined, deps: MemoryCanvasDeps, teamId?: string, joinedUserId?: string): Promise<CanvasOutcome>;
|
|
38
|
+
/** What woke a canvas decision: a memory write, or one of the two trust events. */
|
|
39
|
+
export type CanvasTrigger = "memory-write" | "member-joined" | "channel-shared";
|
|
40
|
+
/**
|
|
41
|
+
* Recheck ONE channel's canvas after a trust event (a person joined it, or it was shared). The front
|
|
42
|
+
* route schedules it, and it writes one `memory.canvas_refresh` row.
|
|
43
|
+
*
|
|
44
|
+
* 🔴 IT RUNS IN EVERY CHANNEL, NOT ONLY A CANVAS-SHOWING DOMAIN'S. A channel whose domain no longer
|
|
45
|
+
* shows a canvas — `memoryCanvas: false`, no `memory`, or a channel moved out of every domain — may
|
|
46
|
+
* still hold the canvas the app wrote while it did. Skipping it would leave that canvas up with no gate
|
|
47
|
+
* ever running on it again, so such a channel gets the clear-only path (`cleared-disabled` when the
|
|
48
|
+
* app's canvas was there). The cost is two Slack reads per person joining a channel with no canvas.
|
|
49
|
+
*/
|
|
50
|
+
export declare function recheckChannelCanvas(input: {
|
|
51
|
+
readonly org: string;
|
|
52
|
+
readonly teamId: string;
|
|
53
|
+
readonly channelId: string;
|
|
54
|
+
readonly trigger: Exclude<CanvasTrigger, "memory-write">;
|
|
55
|
+
readonly joinedUserId?: string;
|
|
56
|
+
}, deps: MemoryCanvasDeps): Promise<CanvasOutcome>;
|
|
57
|
+
/**
|
|
58
|
+
* Refresh every canvas a write to `event.silo` made stale. The observer the provider registers.
|
|
59
|
+
* A domain with `memoryCanvas: false` gets no new canvas, and the app takes its own old canvas down.
|
|
60
|
+
* When no domain names the silo — the common case — the refresh costs no Slack call at all.
|
|
61
|
+
*/
|
|
62
|
+
export declare function refreshMemoryCanvases(event: MemoryWriteEvent, deps: MemoryCanvasDeps): Promise<void>;
|
|
63
|
+
/** Coalesce refreshes for one org and silo: a call during a run schedules exactly one more run. */
|
|
64
|
+
export declare function coalescedRefresh(event: MemoryWriteEvent, deps: MemoryCanvasDeps): Promise<void>;
|
|
65
|
+
//# sourceMappingURL=slack-memory-canvas.d.ts.map
|