@crewhaus/channel-adapter-slack 0.4.0 → 0.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -88,14 +88,59 @@ export type ParsedInbound = {
88
88
  } | {
89
89
  readonly kind: "skip";
90
90
  };
91
+ /**
92
+ * An UNSIGNED, GET-based subscription handshake — the shape Meta (WhatsApp)
93
+ * uses to verify a callback URL before it will deliver any webhook. There is
94
+ * no body and no HMAC header to check, so the gateway cannot route it through
95
+ * `verify()`/`parseInbound()`; instead the platform proves itself with a
96
+ * shared verify token carried in the query string.
97
+ *
98
+ * The gateway hands the parsed request URL (query string included) plus the
99
+ * headers; the adapter decides. Slack and Discord verify inside a signed POST
100
+ * body (`{kind:"challenge"}` from `parseInbound`) and leave `handshake`
101
+ * undefined — for them the gateway's behaviour is unchanged.
102
+ */
103
+ export type HandshakeRequest = {
104
+ readonly headers: Headers;
105
+ readonly url: URL;
106
+ };
107
+ /**
108
+ * `{kind:"challenge"}` ⇒ the gateway echoes `challenge` back with 200; any
109
+ * other outcome ⇒ 403. An adapter that cannot authenticate the caller (bad
110
+ * token, missing params, or no verify token configured on this daemon) MUST
111
+ * return `{kind:"reject"}` — echoing an unauthenticated challenge would let
112
+ * anyone bind their own Meta app to this daemon's callback URL.
113
+ */
114
+ export type HandshakeResult = {
115
+ readonly kind: "challenge";
116
+ readonly challenge: string;
117
+ } | {
118
+ readonly kind: "reject";
119
+ };
91
120
  export interface ChannelAdapter {
92
121
  readonly id: string;
93
122
  verify(req: RawRequest): boolean;
94
123
  parseInbound(req: RawRequest): ParsedInbound;
124
+ /**
125
+ * Answer an unsigned GET subscription handshake (Meta's `hub.challenge`
126
+ * flow). Optional — adapters whose platform has no such handshake leave it
127
+ * undefined and the gateway falls through to the normal signed path.
128
+ */
129
+ handshake?(req: HandshakeRequest): HandshakeResult;
130
+ /**
131
+ * D40 — post the reply; when the channel API returns a post receipt, the
132
+ * adapter surfaces the posted message's ts as `{ messageTs }` so the
133
+ * generated session-router can append its outbound-ts reaction join
134
+ * (exact-turn 👍/👎 attribution). Resolving void stays legal — an adapter
135
+ * without a receipt simply produces no join line, and reactions on its
136
+ * replies take the router's per-key fallback.
137
+ */
95
138
  sendReply(args: {
96
139
  event: InboundEvent;
97
140
  text: string;
98
- }): Promise<void>;
141
+ }): Promise<{
142
+ messageTs?: string;
143
+ } | void>;
99
144
  setTyping(args: {
100
145
  event: InboundEvent;
101
146
  }): Promise<void>;
package/dist/index.js CHANGED
@@ -171,6 +171,10 @@ export function createSlackAdapter(config, opts = {}) {
171
171
  if (body.ok === false) {
172
172
  throw new SlackAdapterError(`chat.postMessage error: ${body.error ?? "unknown"}`);
173
173
  }
174
+ // D40 — surface the posted message's ts (mirroring postApproval's
175
+ // receipt) so the session-router can record the outbound-ts → turn
176
+ // join for exact-turn reaction attribution.
177
+ return typeof body.ts === "string" ? { messageTs: body.ts } : undefined;
174
178
  }
175
179
  },
176
180
  async setTyping(_args) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crewhaus/channel-adapter-slack",
3
- "version": "0.4.0",
3
+ "version": "0.4.2",
4
4
  "type": "module",
5
5
  "description": "Slack channel adapter: webhook signature verification, event parsing, reply-to-thread (Section 12)",
6
6
  "main": "dist/index.js",
@@ -15,7 +15,7 @@
15
15
  "test": "bun test src"
16
16
  },
17
17
  "dependencies": {
18
- "@crewhaus/errors": "0.4.0"
18
+ "@crewhaus/errors": "0.4.2"
19
19
  },
20
20
  "license": "Apache-2.0",
21
21
  "author": {