@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.
Files changed (111) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +27 -0
  3. package/dist/ask.d.ts +69 -0
  4. package/dist/ask.d.ts.map +1 -0
  5. package/dist/ask.js +94 -0
  6. package/dist/ask.js.map +1 -0
  7. package/dist/commands.d.ts +41 -0
  8. package/dist/commands.d.ts.map +1 -0
  9. package/dist/commands.js +54 -0
  10. package/dist/commands.js.map +1 -0
  11. package/dist/index.d.ts +19 -0
  12. package/dist/index.d.ts.map +1 -0
  13. package/dist/index.js +19 -0
  14. package/dist/index.js.map +1 -0
  15. package/dist/kernel.d.ts +207 -0
  16. package/dist/kernel.d.ts.map +1 -0
  17. package/dist/kernel.js +883 -0
  18. package/dist/kernel.js.map +1 -0
  19. package/dist/render.d.ts +43 -0
  20. package/dist/render.d.ts.map +1 -0
  21. package/dist/render.js +192 -0
  22. package/dist/render.js.map +1 -0
  23. package/dist/slack/api.d.ts +207 -0
  24. package/dist/slack/api.d.ts.map +1 -0
  25. package/dist/slack/api.js +249 -0
  26. package/dist/slack/api.js.map +1 -0
  27. package/dist/slack/blocks.d.ts +36 -0
  28. package/dist/slack/blocks.d.ts.map +1 -0
  29. package/dist/slack/blocks.js +161 -0
  30. package/dist/slack/blocks.js.map +1 -0
  31. package/dist/slack/ids.d.ts +40 -0
  32. package/dist/slack/ids.d.ts.map +1 -0
  33. package/dist/slack/ids.js +47 -0
  34. package/dist/slack/ids.js.map +1 -0
  35. package/dist/slack/index.d.ts +24 -0
  36. package/dist/slack/index.d.ts.map +1 -0
  37. package/dist/slack/index.js +18 -0
  38. package/dist/slack/index.js.map +1 -0
  39. package/dist/slack/manifest.d.ts +23 -0
  40. package/dist/slack/manifest.d.ts.map +1 -0
  41. package/dist/slack/manifest.js +102 -0
  42. package/dist/slack/manifest.js.map +1 -0
  43. package/dist/slack/normalize.d.ts +35 -0
  44. package/dist/slack/normalize.d.ts.map +1 -0
  45. package/dist/slack/normalize.js +63 -0
  46. package/dist/slack/normalize.js.map +1 -0
  47. package/dist/slack/payloads.d.ts +36 -0
  48. package/dist/slack/payloads.d.ts.map +1 -0
  49. package/dist/slack/payloads.js +80 -0
  50. package/dist/slack/payloads.js.map +1 -0
  51. package/dist/slack/slackChannel.d.ts +152 -0
  52. package/dist/slack/slackChannel.d.ts.map +1 -0
  53. package/dist/slack/slackChannel.js +650 -0
  54. package/dist/slack/slackChannel.js.map +1 -0
  55. package/dist/slack/stream.d.ts +88 -0
  56. package/dist/slack/stream.d.ts.map +1 -0
  57. package/dist/slack/stream.js +418 -0
  58. package/dist/slack/stream.js.map +1 -0
  59. package/dist/slack/verify.d.ts +20 -0
  60. package/dist/slack/verify.d.ts.map +1 -0
  61. package/dist/slack/verify.js +32 -0
  62. package/dist/slack/verify.js.map +1 -0
  63. package/dist/telegram/api.d.ts +110 -0
  64. package/dist/telegram/api.d.ts.map +1 -0
  65. package/dist/telegram/api.js +170 -0
  66. package/dist/telegram/api.js.map +1 -0
  67. package/dist/telegram/format.d.ts +26 -0
  68. package/dist/telegram/format.d.ts.map +1 -0
  69. package/dist/telegram/format.js +56 -0
  70. package/dist/telegram/format.js.map +1 -0
  71. package/dist/telegram/html.d.ts +11 -0
  72. package/dist/telegram/html.d.ts.map +1 -0
  73. package/dist/telegram/html.js +35 -0
  74. package/dist/telegram/html.js.map +1 -0
  75. package/dist/telegram/index.d.ts +15 -0
  76. package/dist/telegram/index.d.ts.map +1 -0
  77. package/dist/telegram/index.js +15 -0
  78. package/dist/telegram/index.js.map +1 -0
  79. package/dist/telegram/stream.d.ts +59 -0
  80. package/dist/telegram/stream.d.ts.map +1 -0
  81. package/dist/telegram/stream.js +185 -0
  82. package/dist/telegram/stream.js.map +1 -0
  83. package/dist/telegram/telegramChannel.d.ts +136 -0
  84. package/dist/telegram/telegramChannel.d.ts.map +1 -0
  85. package/dist/telegram/telegramChannel.js +277 -0
  86. package/dist/telegram/telegramChannel.js.map +1 -0
  87. package/dist/telegram/verify.d.ts +7 -0
  88. package/dist/telegram/verify.d.ts.map +1 -0
  89. package/dist/telegram/verify.js +14 -0
  90. package/dist/telegram/verify.js.map +1 -0
  91. package/dist/types.d.ts +240 -0
  92. package/dist/types.d.ts.map +1 -0
  93. package/dist/types.js +12 -0
  94. package/dist/types.js.map +1 -0
  95. package/dist/whatsapp/api.d.ts +59 -0
  96. package/dist/whatsapp/api.d.ts.map +1 -0
  97. package/dist/whatsapp/api.js +154 -0
  98. package/dist/whatsapp/api.js.map +1 -0
  99. package/dist/whatsapp/index.d.ts +13 -0
  100. package/dist/whatsapp/index.d.ts.map +1 -0
  101. package/dist/whatsapp/index.js +13 -0
  102. package/dist/whatsapp/index.js.map +1 -0
  103. package/dist/whatsapp/verify.d.ts +17 -0
  104. package/dist/whatsapp/verify.d.ts.map +1 -0
  105. package/dist/whatsapp/verify.js +32 -0
  106. package/dist/whatsapp/verify.js.map +1 -0
  107. package/dist/whatsapp/whatsappChannel.d.ts +104 -0
  108. package/dist/whatsapp/whatsappChannel.d.ts.map +1 -0
  109. package/dist/whatsapp/whatsappChannel.js +217 -0
  110. package/dist/whatsapp/whatsappChannel.js.map +1 -0
  111. 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
+ // `&lt;` in user text must not become parseable tokens first.
39
+ out = out.replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&amp;/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":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAG7D,OAAO,EAAY,KAAK,eAAe,EAAE,KAAK,UAAU,EAAE,MAAM,UAAU,CAAC;AAe3E;;;;;;;;;;;;;;;;;;;;;GAqBG;AAIH,gEAAgE;AAChE,MAAM,WAAW,aAAa;IAC5B,wEAAwE;IACxE,eAAe,EAAE,MAAM,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC/B,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;IACnE,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAAC;IACnD,8DAA8D;IAC9D,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3C,2EAA2E;IAC3E,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACjE;AAED,MAAM,WAAW,kBAAkB,CAAC,CAAC;IACnC,qDAAqD;IACrD,KAAK,EAAE,MAAM,CAAC;IACd,6EAA6E;IAC7E,KAAK,EAAE,MAAM,CAAC;IAEd,gGAAgG;IAChG,SAAS,EAAE,CAAC,CAAC;IACb,4DAA4D;IAC5D,QAAQ,EAAE,UAAU,CAAC;IACrB,OAAO,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC;IAC1B;;;;OAIG;IACH,SAAS,EAAE,MAAM,CAAC;IAClB,yEAAyE;IACzE,MAAM,EAAE,MAAM,CAAC;IAEf,aAAa,EAAE,MAAM,GAAG,CAAC,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;IACzD;;;;OAIG;IACH,SAAS,CAAC,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC;IAE1C;;;;;OAKG;IACH,SAAS,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC5D;;;;;OAKG;IACH,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACvD,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/D,oCAAoC;IACpC,OAAO,EAAE,gBAAgB,CAAC;IAE1B;;;;;OAKG;IACH,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE;QACrB,MAAM,EAAE,MAAM,CAAC;QACf,MAAM,EAAE,MAAM,CAAC;QACf,SAAS,EAAE,MAAM,CAAC;QAClB,UAAU,EAAE,MAAM,CAAC;QACnB;8EACsE;QACtE,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,MAAM,EAAE,MAAM,CAAC;KAChB,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;IAE7B,8EAA8E;IAC9E,YAAY,CAAC,EAAE,CACb,cAAc,EAAE,MAAM,EACtB,GAAG,EAAE;QACH,MAAM,EAAE,MAAM,CAAC;QACf,SAAS,EAAE,MAAM,CAAC;QAClB,QAAQ,EAAE,MAAM,CAAC;QACjB,MAAM,EAAE,MAAM,CAAC;QACf,UAAU,EAAE,MAAM,CAAC;KACpB,KACE,MAAM,CAAC;IAEZ,4EAA4E;IAC5E,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE;QACnB,IAAI,EAAE,gBAAgB,GAAG,iBAAiB,CAAC;QAC3C,MAAM,EAAE,MAAM,CAAC;KAChB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAEpB;;;;;OAKG;IACH,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE;QACpB,MAAM,EAAE,MAAM,CAAC;QACf,IAAI,EAAE,QAAQ,GAAG,UAAU,CAAC;QAC5B,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAEpB,8CAA8C;IAC9C,UAAU,CAAC,EAAE,eAAe,CAAC;CAC9B;AAsCD,wBAAgB,YAAY,CAAC,CAAC,EAAE,MAAM,EAAE,kBAAkB,CAAC,CAAC,CAAC;6BAyhBrB,OAAO,KAAG,OAAO,CAAC,QAAQ,CAAC;sBAqFlC;QAC7B,OAAO,EAAE,MAAM,CAAC;QAChB,2DAA2D;QAC3D,IAAI,EAAE,MAAM,CAAC;QACb,4EAA4E;QAC5E,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,MAAM,EAAE,MAAM,CAAC;QACf,MAAM,EAAE,MAAM,CAAC;QACf,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,SAAS,CAAC,EAAE,CAAC,CAAC;KACf,KAAG,OAAO,CAAC,IAAI,CAAC;0BA4BkB;QACjC,MAAM,EAAE,MAAM,CAAC;QACf,UAAU,EAAE,MAAM,CAAC;QACnB,MAAM,EAAE,MAAM,CAAC;QACf,6DAA6D;QAC7D,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,SAAS,CAAC,EAAE,CAAC,CAAC;KACf,KAAG,OAAO,CAAC,IAAI,CAAC;EAsDlB"}