@intentface/latch-channels 0.9.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/LICENSE +21 -0
- package/README.md +27 -0
- package/dist/ask.d.ts +69 -0
- package/dist/ask.d.ts.map +1 -0
- package/dist/ask.js +94 -0
- package/dist/ask.js.map +1 -0
- package/dist/commands.d.ts +41 -0
- package/dist/commands.d.ts.map +1 -0
- package/dist/commands.js +54 -0
- package/dist/commands.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- package/dist/kernel.d.ts +207 -0
- package/dist/kernel.d.ts.map +1 -0
- package/dist/kernel.js +883 -0
- package/dist/kernel.js.map +1 -0
- package/dist/render.d.ts +43 -0
- package/dist/render.d.ts.map +1 -0
- package/dist/render.js +192 -0
- package/dist/render.js.map +1 -0
- package/dist/slack/api.d.ts +207 -0
- package/dist/slack/api.d.ts.map +1 -0
- package/dist/slack/api.js +249 -0
- package/dist/slack/api.js.map +1 -0
- package/dist/slack/blocks.d.ts +36 -0
- package/dist/slack/blocks.d.ts.map +1 -0
- package/dist/slack/blocks.js +161 -0
- package/dist/slack/blocks.js.map +1 -0
- package/dist/slack/ids.d.ts +40 -0
- package/dist/slack/ids.d.ts.map +1 -0
- package/dist/slack/ids.js +47 -0
- package/dist/slack/ids.js.map +1 -0
- package/dist/slack/index.d.ts +24 -0
- package/dist/slack/index.d.ts.map +1 -0
- package/dist/slack/index.js +18 -0
- package/dist/slack/index.js.map +1 -0
- package/dist/slack/manifest.d.ts +23 -0
- package/dist/slack/manifest.d.ts.map +1 -0
- package/dist/slack/manifest.js +102 -0
- package/dist/slack/manifest.js.map +1 -0
- package/dist/slack/normalize.d.ts +35 -0
- package/dist/slack/normalize.d.ts.map +1 -0
- package/dist/slack/normalize.js +63 -0
- package/dist/slack/normalize.js.map +1 -0
- package/dist/slack/payloads.d.ts +36 -0
- package/dist/slack/payloads.d.ts.map +1 -0
- package/dist/slack/payloads.js +80 -0
- package/dist/slack/payloads.js.map +1 -0
- package/dist/slack/slackChannel.d.ts +152 -0
- package/dist/slack/slackChannel.d.ts.map +1 -0
- package/dist/slack/slackChannel.js +650 -0
- package/dist/slack/slackChannel.js.map +1 -0
- package/dist/slack/stream.d.ts +88 -0
- package/dist/slack/stream.d.ts.map +1 -0
- package/dist/slack/stream.js +418 -0
- package/dist/slack/stream.js.map +1 -0
- package/dist/slack/verify.d.ts +20 -0
- package/dist/slack/verify.d.ts.map +1 -0
- package/dist/slack/verify.js +32 -0
- package/dist/slack/verify.js.map +1 -0
- package/dist/telegram/api.d.ts +110 -0
- package/dist/telegram/api.d.ts.map +1 -0
- package/dist/telegram/api.js +170 -0
- package/dist/telegram/api.js.map +1 -0
- package/dist/telegram/format.d.ts +26 -0
- package/dist/telegram/format.d.ts.map +1 -0
- package/dist/telegram/format.js +56 -0
- package/dist/telegram/format.js.map +1 -0
- package/dist/telegram/html.d.ts +11 -0
- package/dist/telegram/html.d.ts.map +1 -0
- package/dist/telegram/html.js +35 -0
- package/dist/telegram/html.js.map +1 -0
- package/dist/telegram/index.d.ts +15 -0
- package/dist/telegram/index.d.ts.map +1 -0
- package/dist/telegram/index.js +15 -0
- package/dist/telegram/index.js.map +1 -0
- package/dist/telegram/stream.d.ts +59 -0
- package/dist/telegram/stream.d.ts.map +1 -0
- package/dist/telegram/stream.js +185 -0
- package/dist/telegram/stream.js.map +1 -0
- package/dist/telegram/telegramChannel.d.ts +136 -0
- package/dist/telegram/telegramChannel.d.ts.map +1 -0
- package/dist/telegram/telegramChannel.js +277 -0
- package/dist/telegram/telegramChannel.js.map +1 -0
- package/dist/telegram/verify.d.ts +7 -0
- package/dist/telegram/verify.d.ts.map +1 -0
- package/dist/telegram/verify.js +14 -0
- package/dist/telegram/verify.js.map +1 -0
- package/dist/types.d.ts +240 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +12 -0
- package/dist/types.js.map +1 -0
- package/dist/whatsapp/api.d.ts +59 -0
- package/dist/whatsapp/api.d.ts.map +1 -0
- package/dist/whatsapp/api.js +154 -0
- package/dist/whatsapp/api.js.map +1 -0
- package/dist/whatsapp/index.d.ts +13 -0
- package/dist/whatsapp/index.d.ts.map +1 -0
- package/dist/whatsapp/index.js +13 -0
- package/dist/whatsapp/index.js.map +1 -0
- package/dist/whatsapp/verify.d.ts +17 -0
- package/dist/whatsapp/verify.d.ts.map +1 -0
- package/dist/whatsapp/verify.js +32 -0
- package/dist/whatsapp/verify.js.map +1 -0
- package/dist/whatsapp/whatsappChannel.d.ts +104 -0
- package/dist/whatsapp/whatsappChannel.d.ts.map +1 -0
- package/dist/whatsapp/whatsappChannel.js +217 -0
- package/dist/whatsapp/whatsappChannel.js.map +1 -0
- package/package.json +68 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../../src/slack/manifest.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,WAAW,kBAAkB;IACjC,4DAA4D;IAC5D,WAAW,EAAE,MAAM,CAAC;IACpB,wDAAwD;IACxD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,8EAA8E;IAC9E,UAAU,EAAE,MAAM,CAAC;IACnB,6EAA6E;IAC7E,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,iFAAiF;AACjF,eAAO,MAAM,gBAAgB,UAoB5B,CAAC;AAyBF,2DAA2D;AAC3D,wBAAgB,aAAa,CAAC,KAAK,EAAE,kBAAkB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CA2ChF;AAED,qFAAqF;AACrF,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAG/E"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generate the Slack **app manifest** for a single agent's dedicated app. Used
|
|
3
|
+
* both ways: posted to `apps.manifest.create` (config-token / "one-button"
|
|
4
|
+
* flow), or shown to the user with a "Create from manifest" deep-link (manual
|
|
5
|
+
* flow). One app per agent → its own bot user and @-handle.
|
|
6
|
+
*/
|
|
7
|
+
/** Bot scopes for the supported feature set (mentions, DMs, posting, status). */
|
|
8
|
+
export const SLACK_BOT_SCOPES = [
|
|
9
|
+
"app_mentions:read",
|
|
10
|
+
"chat:write",
|
|
11
|
+
"chat:write.customize",
|
|
12
|
+
"im:history",
|
|
13
|
+
"im:read",
|
|
14
|
+
"im:write",
|
|
15
|
+
"channels:history",
|
|
16
|
+
// List public channels — pickers and the mirage sandbox's read-only /slack
|
|
17
|
+
// mount (its transport guard forces types=public_channel regardless).
|
|
18
|
+
"channels:read",
|
|
19
|
+
"groups:history",
|
|
20
|
+
// Membership checks on private channels for the requester-visibility gate
|
|
21
|
+
// (conversations.members). Listing stays public-only above.
|
|
22
|
+
"groups:read",
|
|
23
|
+
// Reserved for a later per-binding "auto-join public channels" opt-in —
|
|
24
|
+
// granted now so enabling it won't need a reinstall.
|
|
25
|
+
"channels:join",
|
|
26
|
+
"assistant:write",
|
|
27
|
+
"commands",
|
|
28
|
+
];
|
|
29
|
+
const BOT_SCOPES = SLACK_BOT_SCOPES;
|
|
30
|
+
/**
|
|
31
|
+
* Slack's "short description" limit as the CREATE-APP WIZARD enforces it. The
|
|
32
|
+
* manifest reference documents 140 for `display_information.description`, but
|
|
33
|
+
* the wizard rejects a 124-char description as "too long" (observed live) and
|
|
34
|
+
* leaves its confirm button disabled until it fits — the app-listing limit of
|
|
35
|
+
* 100 is the one that actually gates creation. Trimmed at a word boundary so a
|
|
36
|
+
* long agent description doesn't get cut mid-word.
|
|
37
|
+
*/
|
|
38
|
+
const DESCRIPTION_MAX = 100;
|
|
39
|
+
function trimDescription(text) {
|
|
40
|
+
if (text.length <= DESCRIPTION_MAX)
|
|
41
|
+
return text;
|
|
42
|
+
let cut = text.slice(0, DESCRIPTION_MAX - 1);
|
|
43
|
+
// Never end the cut on a high surrogate — slicing inside an astral pair
|
|
44
|
+
// (an emoji, say) would ship a lone surrogate in the manifest.
|
|
45
|
+
if (/[\uD800-\uDBFF]$/.test(cut))
|
|
46
|
+
cut = cut.slice(0, -1);
|
|
47
|
+
// Break at the LAST whitespace of any kind (newlines, tabs, NBSP — not just
|
|
48
|
+
// spaces), unless that would give up more than half the budget.
|
|
49
|
+
const lastWs = /\s\S*$/.exec(cut)?.index ?? -1;
|
|
50
|
+
return `${(lastWs > DESCRIPTION_MAX / 2 ? cut.slice(0, lastWs) : cut).trimEnd()}…`;
|
|
51
|
+
}
|
|
52
|
+
/** Build the Slack manifest object (JSON-serializable). */
|
|
53
|
+
export function slackManifest(input) {
|
|
54
|
+
const name = input.displayName.slice(0, 35); // Slack app name limit.
|
|
55
|
+
return {
|
|
56
|
+
display_information: {
|
|
57
|
+
name,
|
|
58
|
+
description: trimDescription(input.description ?? `${name} on Latch`),
|
|
59
|
+
},
|
|
60
|
+
features: {
|
|
61
|
+
bot_user: { display_name: name, always_online: true },
|
|
62
|
+
// Agent Sessions (the "Agents" pane). Replaces the deprecated
|
|
63
|
+
// assistant_view (sunset Feb 2027); Slack treats the flip as ONE-WAY.
|
|
64
|
+
agent_view: { agent_description: `Chat with ${name}`.slice(0, 200) },
|
|
65
|
+
},
|
|
66
|
+
oauth_config: {
|
|
67
|
+
...(input.redirectUrl ? { redirect_urls: [input.redirectUrl] } : {}),
|
|
68
|
+
scopes: { bot: BOT_SCOPES },
|
|
69
|
+
},
|
|
70
|
+
settings: {
|
|
71
|
+
event_subscriptions: {
|
|
72
|
+
request_url: input.requestUrl,
|
|
73
|
+
// message.channels/groups power thread-following (continue without
|
|
74
|
+
// re-@-mentioning); mentions still come via app_mention. Subscribing
|
|
75
|
+
// to agent_session_stopped is what makes the native Stop button
|
|
76
|
+
// appear; tokens_revoked/app_uninstalled let the host disable dead
|
|
77
|
+
// bindings instead of erroring forever.
|
|
78
|
+
bot_events: [
|
|
79
|
+
"app_mention",
|
|
80
|
+
"message.im",
|
|
81
|
+
"message.channels",
|
|
82
|
+
"message.groups",
|
|
83
|
+
"app_home_opened",
|
|
84
|
+
"app_context_changed",
|
|
85
|
+
"agent_session_stopped",
|
|
86
|
+
"agent_session_title_changed",
|
|
87
|
+
"tokens_revoked",
|
|
88
|
+
"app_uninstalled",
|
|
89
|
+
],
|
|
90
|
+
},
|
|
91
|
+
interactivity: { is_enabled: true, request_url: input.requestUrl },
|
|
92
|
+
org_deploy_enabled: false,
|
|
93
|
+
socket_mode_enabled: false,
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
/** A pre-filled "Create New App → From a manifest" deep-link for the manual flow. */
|
|
98
|
+
export function createFromManifestUrl(manifest) {
|
|
99
|
+
const json = encodeURIComponent(JSON.stringify(manifest));
|
|
100
|
+
return `https://api.slack.com/apps?new_app=1&manifest_json=${json}`;
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=manifest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest.js","sourceRoot":"","sources":["../../src/slack/manifest.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Inbound message normalization — what the model sees of a Slack message.
|
|
3
|
+
*
|
|
4
|
+
* Slack text arrives with mrkdwn tokens (`<@U…>`, `<#C…|name>`, `<url|label>`)
|
|
5
|
+
* and HTML-escaped entities. v1 only stripped a LEADING bot mention and used a
|
|
6
|
+
* `<@` substring test to gate thread replies — so mentioning a colleague
|
|
7
|
+
* mid-message silently dropped the message. v2 rewrites every token to a plain
|
|
8
|
+
* representation and gates on message shape, never on text content.
|
|
9
|
+
*
|
|
10
|
+
* No `users:read` scope: a user mention without an inline label renders as the
|
|
11
|
+
* raw id (`@U123`) — a stable handle the model can echo back, which the
|
|
12
|
+
* outbound side re-links.
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* Turn Slack mrkdwn source text into plain prompt text.
|
|
16
|
+
* Bot self-mentions vanish; everything else stays human-readable.
|
|
17
|
+
*/
|
|
18
|
+
export declare function normalizeSlackText(text: string, botUserId?: string): string;
|
|
19
|
+
/**
|
|
20
|
+
* Is this `message`/`app_mention` event a human message the channel should
|
|
21
|
+
* consider running a turn for? Shape checks only — the caller layers admission
|
|
22
|
+
* (dedupe, ownership, authorization) on top.
|
|
23
|
+
*
|
|
24
|
+
* The subtype ALLOWLIST replaces v1's blanket `!subtype` + separate
|
|
25
|
+
* `bot_message` checks: everything not explicitly allowed (edits, joins,
|
|
26
|
+
* bot_message, channel_topic, …) is out. The only self-check is the bot user
|
|
27
|
+
* id — v2 requires hosts to know it (captured at install), so the weaker
|
|
28
|
+
* `bot_id`-presence heuristic covers other bots, not identity.
|
|
29
|
+
*/
|
|
30
|
+
export declare function isRenderableUserMessage(ev: {
|
|
31
|
+
user?: string;
|
|
32
|
+
subtype?: string;
|
|
33
|
+
bot_id?: string;
|
|
34
|
+
}, botUserId: string): boolean;
|
|
35
|
+
//# sourceMappingURL=normalize.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"normalize.d.ts","sourceRoot":"","sources":["../../src/slack/normalize.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAKH;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CA0B3E;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,uBAAuB,CACrC,EAAE,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,EACxD,SAAS,EAAE,MAAM,GAChB,OAAO,CAKT"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Inbound message normalization — what the model sees of a Slack message.
|
|
3
|
+
*
|
|
4
|
+
* Slack text arrives with mrkdwn tokens (`<@U…>`, `<#C…|name>`, `<url|label>`)
|
|
5
|
+
* and HTML-escaped entities. v1 only stripped a LEADING bot mention and used a
|
|
6
|
+
* `<@` substring test to gate thread replies — so mentioning a colleague
|
|
7
|
+
* mid-message silently dropped the message. v2 rewrites every token to a plain
|
|
8
|
+
* representation and gates on message shape, never on text content.
|
|
9
|
+
*
|
|
10
|
+
* No `users:read` scope: a user mention without an inline label renders as the
|
|
11
|
+
* raw id (`@U123`) — a stable handle the model can echo back, which the
|
|
12
|
+
* outbound side re-links.
|
|
13
|
+
*/
|
|
14
|
+
/** Subtypes that still carry a human message worth running a turn for. */
|
|
15
|
+
const SUBTYPE_ALLOWLIST = new Set(["file_share", "thread_broadcast"]);
|
|
16
|
+
/**
|
|
17
|
+
* Turn Slack mrkdwn source text into plain prompt text.
|
|
18
|
+
* Bot self-mentions vanish; everything else stays human-readable.
|
|
19
|
+
*/
|
|
20
|
+
export function normalizeSlackText(text, botUserId) {
|
|
21
|
+
let out = text;
|
|
22
|
+
// User mentions: `<@U123|display>` → `@display`, `<@U123>` → `@U123`;
|
|
23
|
+
// the bot's own mention (anywhere, not just leading) is removed.
|
|
24
|
+
out = out.replace(/<@([A-Z0-9]+)(?:\|([^>]*))?>/g, (_m, id, label) => {
|
|
25
|
+
if (botUserId && id === botUserId)
|
|
26
|
+
return "";
|
|
27
|
+
return `@${label || id}`;
|
|
28
|
+
});
|
|
29
|
+
// Channel references and broadcast keywords.
|
|
30
|
+
out = out.replace(/<#([A-Z0-9]+)(?:\|([^>]*))?>/g, (_m, id, label) => {
|
|
31
|
+
return `#${label || id}`;
|
|
32
|
+
});
|
|
33
|
+
out = out.replace(/<!(here|channel|everyone)(?:\|[^>]*)?>/g, "@$1");
|
|
34
|
+
// Links: `<url|label>` → `label (url)`, `<url>` → `url`.
|
|
35
|
+
out = out.replace(/<((?:https?|mailto):[^>|]*)\|([^>]*)>/g, "$2 ($1)");
|
|
36
|
+
out = out.replace(/<((?:https?|mailto):[^>|]*)>/g, "$1");
|
|
37
|
+
// Entities LAST — the token forms above use raw angle brackets, escaped
|
|
38
|
+
// `<` in user text must not become parseable tokens first.
|
|
39
|
+
out = out.replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&");
|
|
40
|
+
// Removed mentions leave double spaces behind; collapse within lines only.
|
|
41
|
+
return out.replace(/[^\S\n]{2,}/g, " ").trim();
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Is this `message`/`app_mention` event a human message the channel should
|
|
45
|
+
* consider running a turn for? Shape checks only — the caller layers admission
|
|
46
|
+
* (dedupe, ownership, authorization) on top.
|
|
47
|
+
*
|
|
48
|
+
* The subtype ALLOWLIST replaces v1's blanket `!subtype` + separate
|
|
49
|
+
* `bot_message` checks: everything not explicitly allowed (edits, joins,
|
|
50
|
+
* bot_message, channel_topic, …) is out. The only self-check is the bot user
|
|
51
|
+
* id — v2 requires hosts to know it (captured at install), so the weaker
|
|
52
|
+
* `bot_id`-presence heuristic covers other bots, not identity.
|
|
53
|
+
*/
|
|
54
|
+
export function isRenderableUserMessage(ev, botUserId) {
|
|
55
|
+
if (ev.bot_id)
|
|
56
|
+
return false;
|
|
57
|
+
if (ev.subtype && !SUBTYPE_ALLOWLIST.has(ev.subtype))
|
|
58
|
+
return false;
|
|
59
|
+
if (!ev.user || ev.user === botUserId)
|
|
60
|
+
return false;
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=normalize.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"normalize.js","sourceRoot":"","sources":["../../src/slack/normalize.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,0EAA0E;AAC1E,MAAM,iBAAiB,GAAG,IAAI,GAAG,CAAC,CAAC,YAAY,EAAE,kBAAkB,CAAC,CAAC,CAAC;AAEtE;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAAC,IAAY,EAAE,SAAkB;IACjE,IAAI,GAAG,GAAG,IAAI,CAAC;IAEf,sEAAsE;IACtE,iEAAiE;IACjE,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,+BAA+B,EAAE,CAAC,EAAE,EAAE,EAAU,EAAE,KAAc,EAAE,EAAE;QACpF,IAAI,SAAS,IAAI,EAAE,KAAK,SAAS;YAAE,OAAO,EAAE,CAAC;QAC7C,OAAO,IAAI,KAAK,IAAI,EAAE,EAAE,CAAC;IAC3B,CAAC,CAAC,CAAC;IAEH,6CAA6C;IAC7C,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,+BAA+B,EAAE,CAAC,EAAE,EAAE,EAAU,EAAE,KAAc,EAAE,EAAE;QACpF,OAAO,IAAI,KAAK,IAAI,EAAE,EAAE,CAAC;IAC3B,CAAC,CAAC,CAAC;IACH,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,yCAAyC,EAAE,KAAK,CAAC,CAAC;IAEpE,yDAAyD;IACzD,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,wCAAwC,EAAE,SAAS,CAAC,CAAC;IACvE,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,+BAA+B,EAAE,IAAI,CAAC,CAAC;IAEzD,wEAAwE;IACxE,8DAA8D;IAC9D,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;IAE7E,2EAA2E;IAC3E,OAAO,GAAG,CAAC,OAAO,CAAC,cAAc,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;AACjD,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,uBAAuB,CACrC,EAAwD,EACxD,SAAiB;IAEjB,IAAI,EAAE,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IAC5B,IAAI,EAAE,CAAC,OAAO,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,EAAE,CAAC,OAAO,CAAC;QAAE,OAAO,KAAK,CAAC;IACnE,IAAI,CAAC,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,IAAI,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC;IACpD,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Signed interaction payloads — what rides in a Block Kit button `value`.
|
|
3
|
+
*
|
|
4
|
+
* v1 buttons carried unsigned JSON: trust rested entirely on Slack's request
|
|
5
|
+
* signature over the outer POST, and a payload never expired. v2 payloads are
|
|
6
|
+
* HMAC-signed with a key DERIVED from the app's signing secret (domain-
|
|
7
|
+
* separated, so a signature over a button value can never double as anything
|
|
8
|
+
* else), carry the actor who may click, and expire.
|
|
9
|
+
*
|
|
10
|
+
* Format: `b64url(json) + "." + b64url(HMAC_SHA256(derivedKey, json))`.
|
|
11
|
+
* Slack caps a button `value` at 2000 chars — encode asserts it.
|
|
12
|
+
*/
|
|
13
|
+
export interface ActionPayload {
|
|
14
|
+
v: 2;
|
|
15
|
+
kind: "approval";
|
|
16
|
+
/** The Latch chat the approval belongs to. */
|
|
17
|
+
chatId: string;
|
|
18
|
+
/** The approval to resolve via `runtime.applyApproval`. */
|
|
19
|
+
approvalId: string;
|
|
20
|
+
/** Display only — never trusted for dispatch. */
|
|
21
|
+
tool?: string;
|
|
22
|
+
/** Slack user id whose message triggered the run — the only one who may click. */
|
|
23
|
+
actor: string;
|
|
24
|
+
/** Expiry, epoch seconds. */
|
|
25
|
+
exp: number;
|
|
26
|
+
}
|
|
27
|
+
export declare function encodeAction(payload: Omit<ActionPayload, "v" | "exp"> & {
|
|
28
|
+
exp?: number;
|
|
29
|
+
}, signingSecret: string, nowMs?: number): string;
|
|
30
|
+
/**
|
|
31
|
+
* Decode + verify. Null for anything not exactly right: bad shape, wrong
|
|
32
|
+
* signature, expired, foreign version. Callers treat null as "this button is
|
|
33
|
+
* dead", never as an error to surface loudly (stale buttons are normal).
|
|
34
|
+
*/
|
|
35
|
+
export declare function decodeAction(value: string, signingSecret: string, nowMs?: number): ActionPayload | null;
|
|
36
|
+
//# sourceMappingURL=payloads.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"payloads.d.ts","sourceRoot":"","sources":["../../src/slack/payloads.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,MAAM,WAAW,aAAa;IAC5B,CAAC,EAAE,CAAC,CAAC;IACL,IAAI,EAAE,UAAU,CAAC;IACjB,8CAA8C;IAC9C,MAAM,EAAE,MAAM,CAAC;IACf,2DAA2D;IAC3D,UAAU,EAAE,MAAM,CAAC;IACnB,iDAAiD;IACjD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,kFAAkF;IAClF,KAAK,EAAE,MAAM,CAAC;IACd,6BAA6B;IAC7B,GAAG,EAAE,MAAM,CAAC;CACb;AAcD,wBAAgB,YAAY,CAC1B,OAAO,EAAE,IAAI,CAAC,aAAa,EAAE,GAAG,GAAG,KAAK,CAAC,GAAG;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,EAC5D,aAAa,EAAE,MAAM,EACrB,KAAK,SAAa,GACjB,MAAM,CAcR;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,MAAM,EACb,aAAa,EAAE,MAAM,EACrB,KAAK,SAAa,GACjB,aAAa,GAAG,IAAI,CAkCtB"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Signed interaction payloads — what rides in a Block Kit button `value`.
|
|
3
|
+
*
|
|
4
|
+
* v1 buttons carried unsigned JSON: trust rested entirely on Slack's request
|
|
5
|
+
* signature over the outer POST, and a payload never expired. v2 payloads are
|
|
6
|
+
* HMAC-signed with a key DERIVED from the app's signing secret (domain-
|
|
7
|
+
* separated, so a signature over a button value can never double as anything
|
|
8
|
+
* else), carry the actor who may click, and expire.
|
|
9
|
+
*
|
|
10
|
+
* Format: `b64url(json) + "." + b64url(HMAC_SHA256(derivedKey, json))`.
|
|
11
|
+
* Slack caps a button `value` at 2000 chars — encode asserts it.
|
|
12
|
+
*/
|
|
13
|
+
import { createHmac, timingSafeEqual } from "node:crypto";
|
|
14
|
+
const DOMAIN = "latch-slack-action-v2";
|
|
15
|
+
const DEFAULT_TTL_S = 7 * 24 * 3600;
|
|
16
|
+
/** Key derivation: per-app isolation for free, zero new secrets to manage. */
|
|
17
|
+
function deriveKey(signingSecret) {
|
|
18
|
+
return createHmac("sha256", signingSecret).update(DOMAIN).digest();
|
|
19
|
+
}
|
|
20
|
+
function sign(json, signingSecret) {
|
|
21
|
+
return createHmac("sha256", deriveKey(signingSecret)).update(json).digest();
|
|
22
|
+
}
|
|
23
|
+
export function encodeAction(payload, signingSecret, nowMs = Date.now()) {
|
|
24
|
+
const full = {
|
|
25
|
+
exp: Math.floor(nowMs / 1000) + DEFAULT_TTL_S,
|
|
26
|
+
...payload,
|
|
27
|
+
v: 2,
|
|
28
|
+
};
|
|
29
|
+
const json = Buffer.from(JSON.stringify(full), "utf8");
|
|
30
|
+
const value = `${json.toString("base64url")}.${sign(json, signingSecret).toString("base64url")}`;
|
|
31
|
+
if (value.length > 2000) {
|
|
32
|
+
// A payload is ids + one user id — hitting this means a bug upstream
|
|
33
|
+
// (e.g. someone stuffed tool input in), not an oversized legitimate value.
|
|
34
|
+
throw new Error(`slack action payload too large (${value.length} > 2000)`);
|
|
35
|
+
}
|
|
36
|
+
return value;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Decode + verify. Null for anything not exactly right: bad shape, wrong
|
|
40
|
+
* signature, expired, foreign version. Callers treat null as "this button is
|
|
41
|
+
* dead", never as an error to surface loudly (stale buttons are normal).
|
|
42
|
+
*/
|
|
43
|
+
export function decodeAction(value, signingSecret, nowMs = Date.now()) {
|
|
44
|
+
const dot = value.indexOf(".");
|
|
45
|
+
if (dot < 0)
|
|
46
|
+
return null;
|
|
47
|
+
let json;
|
|
48
|
+
let sig;
|
|
49
|
+
try {
|
|
50
|
+
json = Buffer.from(value.slice(0, dot), "base64url");
|
|
51
|
+
sig = Buffer.from(value.slice(dot + 1), "base64url");
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
const expected = sign(json, signingSecret);
|
|
57
|
+
if (sig.length !== expected.length || !timingSafeEqual(sig, expected))
|
|
58
|
+
return null;
|
|
59
|
+
let parsed;
|
|
60
|
+
try {
|
|
61
|
+
parsed = JSON.parse(json.toString("utf8"));
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
const p = parsed;
|
|
67
|
+
if (!p ||
|
|
68
|
+
p.v !== 2 ||
|
|
69
|
+
p.kind !== "approval" ||
|
|
70
|
+
typeof p.chatId !== "string" ||
|
|
71
|
+
typeof p.approvalId !== "string" ||
|
|
72
|
+
typeof p.actor !== "string" ||
|
|
73
|
+
typeof p.exp !== "number") {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
if (p.exp * 1000 < nowMs)
|
|
77
|
+
return null;
|
|
78
|
+
return p;
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=payloads.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"payloads.js","sourceRoot":"","sources":["../../src/slack/payloads.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import type { AuthResult, ChannelRunOps } from "../types.js";
|
|
2
|
+
import { type SlackApiOptions, type SlackToken } from "./api.js";
|
|
3
|
+
/**
|
|
4
|
+
* The Slack channel, v2. One agent's dedicated Slack app; the host wires
|
|
5
|
+
* `handleRequest` to a single webhook route.
|
|
6
|
+
*
|
|
7
|
+
* Design invariants (each one is a production incident that can't recur):
|
|
8
|
+
*
|
|
9
|
+
* - **ACK first.** Pre-ACK work is signature verification and a minimal
|
|
10
|
+
* parse — nothing else. Every DB/Vault/history read happens in deferred
|
|
11
|
+
* workers. (The v1 pre-ACK chain caused the 3s timeouts whose retries the
|
|
12
|
+
* dedupe layers then had to absorb.)
|
|
13
|
+
* - **Admission is atomic.** One `claimOnce` primitive (TTL'd test-and-set)
|
|
14
|
+
* dedupes deliveries, messages and interactions. One live run per chat is
|
|
15
|
+
* core's storage invariant (`ChatBusyError`), rendered here as the busy
|
|
16
|
+
* notice; a host on an adapter without it may still wire `claimTurn`. No
|
|
17
|
+
* read-then-act preflights.
|
|
18
|
+
* - **Threads are initiator-owned.** The first user to engage a chat owns
|
|
19
|
+
* it; everyone else gets ONE quiet notice. Nobody ever acts with another
|
|
20
|
+
* user's credentials — approvals are actor-bound in a signed payload.
|
|
21
|
+
* - **DMs are continuous.** Un-threaded DM messages share one chat per DM
|
|
22
|
+
* channel; session-pane threads and channel threads get one chat per
|
|
23
|
+
* thread, always scoped to this app.
|
|
24
|
+
*/
|
|
25
|
+
/** Everything Latch remembers about one Slack-anchored chat. */
|
|
26
|
+
export interface SlackChatMeta {
|
|
27
|
+
/** The Slack user whose message created this chat — the only driver. */
|
|
28
|
+
initiatorUserId: string;
|
|
29
|
+
teamId: string;
|
|
30
|
+
createdAt: number;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Host-backed thread registry: ownership + the cross-instance stop flag.
|
|
34
|
+
* `claim` is atomic first-writer-wins and returns the OWNING meta either way.
|
|
35
|
+
*/
|
|
36
|
+
export interface SlackThreadStore {
|
|
37
|
+
claim(chatId: string, meta: SlackChatMeta): Promise<SlackChatMeta>;
|
|
38
|
+
get(chatId: string): Promise<SlackChatMeta | null>;
|
|
39
|
+
/** The viewer pressed Stop (possibly on another instance). */
|
|
40
|
+
markStopped(chatId: string): Promise<void>;
|
|
41
|
+
/** Was a stop raised after `sinceMs`? Polled by the streaming renderer. */
|
|
42
|
+
stoppedSince(chatId: string, sinceMs: number): Promise<boolean>;
|
|
43
|
+
}
|
|
44
|
+
export interface SlackChannelConfig<P> {
|
|
45
|
+
/** The agent this app serves (own-app-per-agent). */
|
|
46
|
+
agent: string;
|
|
47
|
+
/** The Slack app id — the chat-id namespace. Required: no unscoped chats. */
|
|
48
|
+
appId: string;
|
|
49
|
+
/** The binding's owner — the principal a turn runs as unless `authorizeUser` says otherwise. */
|
|
50
|
+
principal: P;
|
|
51
|
+
/** The workspace's bot token (vault-backed in practice). */
|
|
52
|
+
botToken: SlackToken;
|
|
53
|
+
runtime: ChannelRunOps<P>;
|
|
54
|
+
/**
|
|
55
|
+
* The bot's own Slack user id — REQUIRED. The authoritative self-check;
|
|
56
|
+
* v2 has no `bot_id`/`subtype` identity fallback, a binding without it
|
|
57
|
+
* must be reinstalled.
|
|
58
|
+
*/
|
|
59
|
+
botUserId: string;
|
|
60
|
+
/** The installed workspace — `chat.startStream` needs it in channels. */
|
|
61
|
+
teamId: string;
|
|
62
|
+
signingSecret: string | (() => string | Promise<string>);
|
|
63
|
+
/**
|
|
64
|
+
* Keep a serverless function alive for the deferred worker after the ACK.
|
|
65
|
+
* Without it the worker is awaited inline (fine off-serverless, but the ACK
|
|
66
|
+
* then waits on the whole turn).
|
|
67
|
+
*/
|
|
68
|
+
waitUntil?: (p: Promise<unknown>) => void;
|
|
69
|
+
/**
|
|
70
|
+
* Atomic TTL'd test-and-set: TRUE when THIS call claimed the key, false
|
|
71
|
+
* when someone already holds it. One primitive covers delivery retries
|
|
72
|
+
* (`evt:`), the same message arriving as two event shapes (`msg:`),
|
|
73
|
+
* interaction redeliveries (`act:`) and once-ever notices (`note:`).
|
|
74
|
+
*/
|
|
75
|
+
claimOnce: (key: string, ttlMs: number) => Promise<boolean>;
|
|
76
|
+
/**
|
|
77
|
+
* Optional host-side one-turn-per-chat lease (`null` = busy). Core's storage
|
|
78
|
+
* adapter already refuses a second live run per chat (`ChatBusyError`, shown
|
|
79
|
+
* as the busy notice), so this is only for hosts on an adapter without that
|
|
80
|
+
* invariant.
|
|
81
|
+
*/
|
|
82
|
+
claimTurn?: (chatId: string) => Promise<string | null>;
|
|
83
|
+
releaseTurn?: (chatId: string, token: string) => Promise<void>;
|
|
84
|
+
/** Thread ownership + stop flag. */
|
|
85
|
+
threads: SlackThreadStore;
|
|
86
|
+
/**
|
|
87
|
+
* Gate inbound users (identity link, org membership, credential mode).
|
|
88
|
+
* `principal` (when returned with `allowed: true`) runs THIS turn as that
|
|
89
|
+
* principal — how per-user credentials work. `silent` suppresses the
|
|
90
|
+
* refusal notice (disabled bindings shouldn't reply at all).
|
|
91
|
+
*/
|
|
92
|
+
authorizeUser?: (user: {
|
|
93
|
+
teamId: string;
|
|
94
|
+
userId: string;
|
|
95
|
+
channelId: string;
|
|
96
|
+
threadRoot: string;
|
|
97
|
+
/** The asking message's ts — hosts holding the message for a post-link
|
|
98
|
+
* replay keep it so the replay can thread where the question was. */
|
|
99
|
+
anchorTs?: string;
|
|
100
|
+
prompt: string;
|
|
101
|
+
}) => Promise<AuthResult<P>>;
|
|
102
|
+
/** Build the platform authorize URL for a `connect_<name>` OAuth approval. */
|
|
103
|
+
authorizeUrl?: (connectionName: string, ctx: {
|
|
104
|
+
chatId: string;
|
|
105
|
+
channelId: string;
|
|
106
|
+
threadTs: string;
|
|
107
|
+
userId: string;
|
|
108
|
+
approvalId: string;
|
|
109
|
+
}) => string;
|
|
110
|
+
/** `tokens_revoked` / `app_uninstalled` → the host disables the binding. */
|
|
111
|
+
onLifecycle?: (info: {
|
|
112
|
+
type: "tokens_revoked" | "app_uninstalled";
|
|
113
|
+
teamId: string;
|
|
114
|
+
}) => Promise<void>;
|
|
115
|
+
/**
|
|
116
|
+
* Streaming-engine telemetry: called when the engine is decided (`stream`,
|
|
117
|
+
* or `fallback` + Slack's refusal code) and again if a live stream dies
|
|
118
|
+
* mid-turn. Hosts persist the last value so the binding health row can say
|
|
119
|
+
* WHY a bot degraded to post-at-finish mode.
|
|
120
|
+
*/
|
|
121
|
+
onStreamMode?: (info: {
|
|
122
|
+
chatId: string;
|
|
123
|
+
mode: "stream" | "fallback";
|
|
124
|
+
code?: string;
|
|
125
|
+
detail?: string;
|
|
126
|
+
}) => Promise<void>;
|
|
127
|
+
/** Test seam for the Slack Web API client. */
|
|
128
|
+
apiOptions?: SlackApiOptions;
|
|
129
|
+
}
|
|
130
|
+
export declare function slackChannel<P>(config: SlackChannelConfig<P>): {
|
|
131
|
+
handleRequest: (request: Request) => Promise<Response>;
|
|
132
|
+
runMessage: (ctx: {
|
|
133
|
+
channel: string;
|
|
134
|
+
/** Thread root ts, or `dm` for the un-threaded DM chat. */
|
|
135
|
+
root: string;
|
|
136
|
+
/** For `dm` roots: the message ts the replayed turn should thread under. */
|
|
137
|
+
anchorTs?: string;
|
|
138
|
+
userId: string;
|
|
139
|
+
prompt: string;
|
|
140
|
+
context?: string;
|
|
141
|
+
principal?: P;
|
|
142
|
+
}) => Promise<void>;
|
|
143
|
+
resumeApproval: (ctx: {
|
|
144
|
+
chatId: string;
|
|
145
|
+
approvalId: string;
|
|
146
|
+
userId: string;
|
|
147
|
+
/** For `_dm` chats: the thread the connect card lives in. */
|
|
148
|
+
anchorTs?: string;
|
|
149
|
+
principal?: P;
|
|
150
|
+
}) => Promise<void>;
|
|
151
|
+
};
|
|
152
|
+
//# sourceMappingURL=slackChannel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slackChannel.d.ts","sourceRoot":"","sources":["../../src/slack/slackChannel.ts"],"names":[],"mappings":"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"}
|