@kilnai/runtime 0.1.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 (205) hide show
  1. package/dist/a2a/a2a-client.d.ts +6 -0
  2. package/dist/a2a/a2a-client.d.ts.map +1 -0
  3. package/dist/a2a/a2a-client.js +112 -0
  4. package/dist/a2a/a2a-client.js.map +1 -0
  5. package/dist/a2a/index.d.ts +2 -0
  6. package/dist/a2a/index.d.ts.map +1 -0
  7. package/dist/a2a/index.js +3 -0
  8. package/dist/a2a/index.js.map +1 -0
  9. package/dist/channels/api-channel.d.ts +43 -0
  10. package/dist/channels/api-channel.d.ts.map +1 -0
  11. package/dist/channels/api-channel.js +95 -0
  12. package/dist/channels/api-channel.js.map +1 -0
  13. package/dist/channels/channel-registry.d.ts +25 -0
  14. package/dist/channels/channel-registry.d.ts.map +1 -0
  15. package/dist/channels/channel-registry.js +49 -0
  16. package/dist/channels/channel-registry.js.map +1 -0
  17. package/dist/channels/channel-router.d.ts +43 -0
  18. package/dist/channels/channel-router.d.ts.map +1 -0
  19. package/dist/channels/channel-router.js +72 -0
  20. package/dist/channels/channel-router.js.map +1 -0
  21. package/dist/channels/cli-channel.d.ts +19 -0
  22. package/dist/channels/cli-channel.d.ts.map +1 -0
  23. package/dist/channels/cli-channel.js +37 -0
  24. package/dist/channels/cli-channel.js.map +1 -0
  25. package/dist/channels/event-bridge.d.ts +27 -0
  26. package/dist/channels/event-bridge.d.ts.map +1 -0
  27. package/dist/channels/event-bridge.js +83 -0
  28. package/dist/channels/event-bridge.js.map +1 -0
  29. package/dist/channels/index.d.ts +17 -0
  30. package/dist/channels/index.d.ts.map +1 -0
  31. package/dist/channels/index.js +12 -0
  32. package/dist/channels/index.js.map +1 -0
  33. package/dist/channels/message-formatter.d.ts +4 -0
  34. package/dist/channels/message-formatter.d.ts.map +1 -0
  35. package/dist/channels/message-formatter.js +32 -0
  36. package/dist/channels/message-formatter.js.map +1 -0
  37. package/dist/channels/slack-channel.d.ts +32 -0
  38. package/dist/channels/slack-channel.d.ts.map +1 -0
  39. package/dist/channels/slack-channel.js +71 -0
  40. package/dist/channels/slack-channel.js.map +1 -0
  41. package/dist/channels/types.d.ts +27 -0
  42. package/dist/channels/types.d.ts.map +1 -0
  43. package/dist/channels/types.js +12 -0
  44. package/dist/channels/types.js.map +1 -0
  45. package/dist/channels/web-channel.d.ts +37 -0
  46. package/dist/channels/web-channel.d.ts.map +1 -0
  47. package/dist/channels/web-channel.js +112 -0
  48. package/dist/channels/web-channel.js.map +1 -0
  49. package/dist/channels/whatsapp-api.d.ts +20 -0
  50. package/dist/channels/whatsapp-api.d.ts.map +1 -0
  51. package/dist/channels/whatsapp-api.js +44 -0
  52. package/dist/channels/whatsapp-api.js.map +1 -0
  53. package/dist/channels/whatsapp-channel.d.ts +32 -0
  54. package/dist/channels/whatsapp-channel.d.ts.map +1 -0
  55. package/dist/channels/whatsapp-channel.js +85 -0
  56. package/dist/channels/whatsapp-channel.js.map +1 -0
  57. package/dist/gateway/app-resolver.d.ts +11 -0
  58. package/dist/gateway/app-resolver.d.ts.map +1 -0
  59. package/dist/gateway/app-resolver.js +36 -0
  60. package/dist/gateway/app-resolver.js.map +1 -0
  61. package/dist/gateway/approval-registry.d.ts +19 -0
  62. package/dist/gateway/approval-registry.d.ts.map +1 -0
  63. package/dist/gateway/approval-registry.js +49 -0
  64. package/dist/gateway/approval-registry.js.map +1 -0
  65. package/dist/gateway/budget-middleware.d.ts +47 -0
  66. package/dist/gateway/budget-middleware.d.ts.map +1 -0
  67. package/dist/gateway/budget-middleware.js +88 -0
  68. package/dist/gateway/budget-middleware.js.map +1 -0
  69. package/dist/gateway/config-validator.d.ts +31 -0
  70. package/dist/gateway/config-validator.d.ts.map +1 -0
  71. package/dist/gateway/config-validator.js +68 -0
  72. package/dist/gateway/config-validator.js.map +1 -0
  73. package/dist/gateway/delegation-handler.d.ts +53 -0
  74. package/dist/gateway/delegation-handler.d.ts.map +1 -0
  75. package/dist/gateway/delegation-handler.js +257 -0
  76. package/dist/gateway/delegation-handler.js.map +1 -0
  77. package/dist/gateway/delegation-routes.d.ts +7 -0
  78. package/dist/gateway/delegation-routes.d.ts.map +1 -0
  79. package/dist/gateway/delegation-routes.js +48 -0
  80. package/dist/gateway/delegation-routes.js.map +1 -0
  81. package/dist/gateway/dev-inspector.d.ts +2 -0
  82. package/dist/gateway/dev-inspector.d.ts.map +1 -0
  83. package/dist/gateway/dev-inspector.js +355 -0
  84. package/dist/gateway/dev-inspector.js.map +1 -0
  85. package/dist/gateway/dev-orchestrator.d.ts +24 -0
  86. package/dist/gateway/dev-orchestrator.d.ts.map +1 -0
  87. package/dist/gateway/dev-orchestrator.js +71 -0
  88. package/dist/gateway/dev-orchestrator.js.map +1 -0
  89. package/dist/gateway/dev-routes-types.d.ts +39 -0
  90. package/dist/gateway/dev-routes-types.d.ts.map +1 -0
  91. package/dist/gateway/dev-routes-types.js +3 -0
  92. package/dist/gateway/dev-routes-types.js.map +1 -0
  93. package/dist/gateway/dev-routes.d.ts +53 -0
  94. package/dist/gateway/dev-routes.d.ts.map +1 -0
  95. package/dist/gateway/dev-routes.js +217 -0
  96. package/dist/gateway/dev-routes.js.map +1 -0
  97. package/dist/gateway/dev-token-store.d.ts +19 -0
  98. package/dist/gateway/dev-token-store.d.ts.map +1 -0
  99. package/dist/gateway/dev-token-store.js +40 -0
  100. package/dist/gateway/dev-token-store.js.map +1 -0
  101. package/dist/gateway/gateway-routes.d.ts +46 -0
  102. package/dist/gateway/gateway-routes.d.ts.map +1 -0
  103. package/dist/gateway/gateway-routes.js +143 -0
  104. package/dist/gateway/gateway-routes.js.map +1 -0
  105. package/dist/gateway/gateway-server.d.ts +25 -0
  106. package/dist/gateway/gateway-server.d.ts.map +1 -0
  107. package/dist/gateway/gateway-server.js +736 -0
  108. package/dist/gateway/gateway-server.js.map +1 -0
  109. package/dist/gateway/health-registry.d.ts +18 -0
  110. package/dist/gateway/health-registry.d.ts.map +1 -0
  111. package/dist/gateway/health-registry.js +40 -0
  112. package/dist/gateway/health-registry.js.map +1 -0
  113. package/dist/gateway/memory-routes.d.ts +12 -0
  114. package/dist/gateway/memory-routes.d.ts.map +1 -0
  115. package/dist/gateway/memory-routes.js +32 -0
  116. package/dist/gateway/memory-routes.js.map +1 -0
  117. package/dist/gateway/mode-b-routes.d.ts +14 -0
  118. package/dist/gateway/mode-b-routes.d.ts.map +1 -0
  119. package/dist/gateway/mode-b-routes.js +96 -0
  120. package/dist/gateway/mode-b-routes.js.map +1 -0
  121. package/dist/gateway/safety-middleware.d.ts +21 -0
  122. package/dist/gateway/safety-middleware.d.ts.map +1 -0
  123. package/dist/gateway/safety-middleware.js +175 -0
  124. package/dist/gateway/safety-middleware.js.map +1 -0
  125. package/dist/gateway/security-middleware.d.ts +12 -0
  126. package/dist/gateway/security-middleware.d.ts.map +1 -0
  127. package/dist/gateway/security-middleware.js +62 -0
  128. package/dist/gateway/security-middleware.js.map +1 -0
  129. package/dist/gateway/tenant-admin-routes.d.ts +15 -0
  130. package/dist/gateway/tenant-admin-routes.d.ts.map +1 -0
  131. package/dist/gateway/tenant-admin-routes.js +148 -0
  132. package/dist/gateway/tenant-admin-routes.js.map +1 -0
  133. package/dist/gateway/tenant-routes.d.ts +15 -0
  134. package/dist/gateway/tenant-routes.d.ts.map +1 -0
  135. package/dist/gateway/tenant-routes.js +107 -0
  136. package/dist/gateway/tenant-routes.js.map +1 -0
  137. package/dist/gateway/whatsapp-webhook-routes.d.ts +15 -0
  138. package/dist/gateway/whatsapp-webhook-routes.d.ts.map +1 -0
  139. package/dist/gateway/whatsapp-webhook-routes.js +217 -0
  140. package/dist/gateway/whatsapp-webhook-routes.js.map +1 -0
  141. package/dist/gateway/ws-routes.d.ts +19 -0
  142. package/dist/gateway/ws-routes.d.ts.map +1 -0
  143. package/dist/gateway/ws-routes.js +79 -0
  144. package/dist/gateway/ws-routes.js.map +1 -0
  145. package/dist/index.d.ts +64 -0
  146. package/dist/index.d.ts.map +1 -0
  147. package/dist/index.js +44 -0
  148. package/dist/index.js.map +1 -0
  149. package/dist/session/index.d.ts +6 -0
  150. package/dist/session/index.d.ts.map +1 -0
  151. package/dist/session/index.js +4 -0
  152. package/dist/session/index.js.map +1 -0
  153. package/dist/session/mode-b-orchestrator.d.ts +43 -0
  154. package/dist/session/mode-b-orchestrator.d.ts.map +1 -0
  155. package/dist/session/mode-b-orchestrator.js +224 -0
  156. package/dist/session/mode-b-orchestrator.js.map +1 -0
  157. package/dist/session/mode-b-session.d.ts +29 -0
  158. package/dist/session/mode-b-session.d.ts.map +1 -0
  159. package/dist/session/mode-b-session.js +50 -0
  160. package/dist/session/mode-b-session.js.map +1 -0
  161. package/dist/session/session-registry.d.ts +15 -0
  162. package/dist/session/session-registry.d.ts.map +1 -0
  163. package/dist/session/session-registry.js +60 -0
  164. package/dist/session/session-registry.js.map +1 -0
  165. package/dist/tenant/index.d.ts +3 -0
  166. package/dist/tenant/index.d.ts.map +1 -0
  167. package/dist/tenant/index.js +3 -0
  168. package/dist/tenant/index.js.map +1 -0
  169. package/dist/tenant/system-prompt-builder.d.ts +3 -0
  170. package/dist/tenant/system-prompt-builder.d.ts.map +1 -0
  171. package/dist/tenant/system-prompt-builder.js +76 -0
  172. package/dist/tenant/system-prompt-builder.js.map +1 -0
  173. package/dist/tenant/tenant-registry.d.ts +33 -0
  174. package/dist/tenant/tenant-registry.d.ts.map +1 -0
  175. package/dist/tenant/tenant-registry.js +156 -0
  176. package/dist/tenant/tenant-registry.js.map +1 -0
  177. package/dist/trigger/event-listener.d.ts +22 -0
  178. package/dist/trigger/event-listener.d.ts.map +1 -0
  179. package/dist/trigger/event-listener.js +64 -0
  180. package/dist/trigger/event-listener.js.map +1 -0
  181. package/dist/trigger/index.d.ts +10 -0
  182. package/dist/trigger/index.d.ts.map +1 -0
  183. package/dist/trigger/index.js +7 -0
  184. package/dist/trigger/index.js.map +1 -0
  185. package/dist/trigger/scheduler.d.ts +22 -0
  186. package/dist/trigger/scheduler.d.ts.map +1 -0
  187. package/dist/trigger/scheduler.js +77 -0
  188. package/dist/trigger/scheduler.js.map +1 -0
  189. package/dist/trigger/trigger-executor.d.ts +14 -0
  190. package/dist/trigger/trigger-executor.d.ts.map +1 -0
  191. package/dist/trigger/trigger-executor.js +47 -0
  192. package/dist/trigger/trigger-executor.js.map +1 -0
  193. package/dist/trigger/trigger-registry.d.ts +28 -0
  194. package/dist/trigger/trigger-registry.d.ts.map +1 -0
  195. package/dist/trigger/trigger-registry.js +118 -0
  196. package/dist/trigger/trigger-registry.js.map +1 -0
  197. package/dist/trigger/webhook-handler.d.ts +11 -0
  198. package/dist/trigger/webhook-handler.d.ts.map +1 -0
  199. package/dist/trigger/webhook-handler.js +71 -0
  200. package/dist/trigger/webhook-handler.js.map +1 -0
  201. package/dist/utils/hmac.d.ts +15 -0
  202. package/dist/utils/hmac.d.ts.map +1 -0
  203. package/dist/utils/hmac.js +27 -0
  204. package/dist/utils/hmac.js.map +1 -0
  205. package/package.json +53 -0
@@ -0,0 +1,83 @@
1
+ // EventBridge: converts EventBus synchronous push -> AsyncIterable pull
2
+ // Bridges KilnEvent (orchestrator internal) to EngineEvent (channel primitive)
3
+ import { EVENT_LEVEL_MAP, LEVEL_HIERARCHY } from "@kilnai/core";
4
+ /** Convert a KilnEvent to an EngineEvent for channel consumption */
5
+ export function toEngineEvent(event) {
6
+ const { type, timestamp, sessionId, ...rest } = event;
7
+ return {
8
+ type,
9
+ timestamp,
10
+ payload: { sessionId, ...rest },
11
+ };
12
+ }
13
+ /**
14
+ * Bridge between EventBus (synchronous push) and Channel.stream() (AsyncIterable pull).
15
+ * Subscribes to EventBus.onAny(), converts events, and yields them as an async generator.
16
+ * Bounded queue prevents unbounded memory growth if consumer is slower than producer.
17
+ */
18
+ export class EventBridge {
19
+ queue = [];
20
+ maxQueueSize;
21
+ resolve = null;
22
+ done = false;
23
+ unsubscribe = null;
24
+ constructor(maxQueueSize = 1000) {
25
+ this.maxQueueSize = maxQueueSize;
26
+ }
27
+ /**
28
+ * Subscribe to an EventBus and start bridging events.
29
+ * If level is provided, only events matching that level or coarser are bridged.
30
+ */
31
+ connect(eventBus, level) {
32
+ const handler = (event) => {
33
+ if (this.done)
34
+ return;
35
+ if (level) {
36
+ const eventLevel = EVENT_LEVEL_MAP[event.type];
37
+ const allowedLevels = LEVEL_HIERARCHY[level];
38
+ if (!allowedLevels.includes(eventLevel)) {
39
+ return;
40
+ }
41
+ }
42
+ const engineEvent = toEngineEvent(event);
43
+ if (this.queue.length < this.maxQueueSize) {
44
+ this.queue.push(engineEvent);
45
+ }
46
+ // Wake up the async generator if it's waiting
47
+ if (this.resolve) {
48
+ this.resolve();
49
+ this.resolve = null;
50
+ }
51
+ };
52
+ eventBus.onAny(handler);
53
+ this.unsubscribe = () => eventBus.offAny(handler);
54
+ }
55
+ /** Stop bridging and signal the async generator to complete */
56
+ disconnect() {
57
+ this.done = true;
58
+ if (this.unsubscribe) {
59
+ this.unsubscribe();
60
+ this.unsubscribe = null;
61
+ }
62
+ // Wake up pending consumer so it can exit
63
+ if (this.resolve) {
64
+ this.resolve();
65
+ this.resolve = null;
66
+ }
67
+ }
68
+ /** Async generator that yields EngineEvents as they arrive */
69
+ async *events() {
70
+ while (!this.done || this.queue.length > 0) {
71
+ if (this.queue.length > 0) {
72
+ yield this.queue.shift();
73
+ }
74
+ else if (!this.done) {
75
+ // Wait for next event or disconnect
76
+ await new Promise((r) => {
77
+ this.resolve = r;
78
+ });
79
+ }
80
+ }
81
+ }
82
+ }
83
+ //# sourceMappingURL=event-bridge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-bridge.js","sourceRoot":"","sources":["../../src/channels/event-bridge.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ export { EventBridge, toEngineEvent } from "./event-bridge.js";
2
+ export { ChannelRegistry } from "./channel-registry.js";
3
+ export { formatForChannel } from "./message-formatter.js";
4
+ export type { ChannelConfig, ChannelStatus, IdentityMapping, IdentityResolver } from "./types.js";
5
+ export { InMemoryIdentityResolver } from "./types.js";
6
+ export { CliChannel } from "./cli-channel.js";
7
+ export { WebChannel } from "./web-channel.js";
8
+ export type { WebSocketLike } from "./web-channel.js";
9
+ export { ChannelRouter } from "./channel-router.js";
10
+ export type { RouteResult, ChannelRouterRule } from "./channel-router.js";
11
+ export { WhatsAppChannel } from "./whatsapp-channel.js";
12
+ export type { WhatsAppConfig } from "./whatsapp-channel.js";
13
+ export { SlackChannel } from "./slack-channel.js";
14
+ export type { SlackConfig } from "./slack-channel.js";
15
+ export { ApiChannel } from "./api-channel.js";
16
+ export type { SseWriter } from "./api-channel.js";
17
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/channels/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAC/D,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAClG,OAAO,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAC;AACtD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,YAAY,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,YAAY,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAC1E,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,YAAY,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,YAAY,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,YAAY,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC"}
@@ -0,0 +1,12 @@
1
+ // Channel adapters -- multi-platform message delivery
2
+ export { EventBridge, toEngineEvent } from "./event-bridge.js";
3
+ export { ChannelRegistry } from "./channel-registry.js";
4
+ export { formatForChannel } from "./message-formatter.js";
5
+ export { InMemoryIdentityResolver } from "./types.js";
6
+ export { CliChannel } from "./cli-channel.js";
7
+ export { WebChannel } from "./web-channel.js";
8
+ export { ChannelRouter } from "./channel-router.js";
9
+ export { WhatsAppChannel } from "./whatsapp-channel.js";
10
+ export { SlackChannel } from "./slack-channel.js";
11
+ export { ApiChannel } from "./api-channel.js";
12
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/channels/index.ts"],"names":[],"mappings":"AAAA,sDAAsD;AAEtD,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAC/D,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAE1D,OAAO,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAC;AACtD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE9C,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEpD,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAExD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { MessageFormat } from "@kilnai/core";
2
+ /** Adapt content for a specific channel format */
3
+ export declare function formatForChannel(content: string, format: MessageFormat): string;
4
+ //# sourceMappingURL=message-formatter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"message-formatter.d.ts","sourceRoot":"","sources":["../../src/channels/message-formatter.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAKlD,kDAAkD;AAClD,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,GAAG,MAAM,CAc/E"}
@@ -0,0 +1,32 @@
1
+ // MessageFormatter: unified channel output formatting
2
+ // Single source of truth for adapting content to channel-specific formats
3
+ /** WhatsApp Cloud API enforces a 4096-character limit per text message */
4
+ const WHATSAPP_MAX_MESSAGE_LENGTH = 4096;
5
+ /** Adapt content for a specific channel format */
6
+ export function formatForChannel(content, format) {
7
+ switch (format) {
8
+ case "short":
9
+ // Strip markdown, truncate for messaging platforms (WhatsApp, SMS)
10
+ return stripMarkdown(content).slice(0, WHATSAPP_MAX_MESSAGE_LENGTH);
11
+ case "full":
12
+ // Keep full markdown (web, Slack)
13
+ return content;
14
+ case "structured":
15
+ // Return as-is (REST API consumers parse it)
16
+ return content;
17
+ default:
18
+ return content;
19
+ }
20
+ }
21
+ /** Strip basic markdown formatting for plain text channels */
22
+ function stripMarkdown(text) {
23
+ return text
24
+ .replace(/```[\s\S]*?```/g, "[code block]") // code blocks
25
+ .replace(/`([^`]+)`/g, "$1") // inline code
26
+ .replace(/\*\*([^*]+)\*\*/g, "$1") // bold
27
+ .replace(/\*([^*]+)\*/g, "$1") // italic
28
+ .replace(/#+\s/g, "") // headers
29
+ .replace(/\[([^\]]+)\]\([^)]+\)/g, "$1") // links
30
+ .replace(/^[-*]\s/gm, "- "); // list items
31
+ }
32
+ //# sourceMappingURL=message-formatter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"message-formatter.js","sourceRoot":"","sources":["../../src/channels/message-formatter.ts"],"names":[],"mappings":"AAAA,sDAAsD;AACtD,0EAA0E;AAI1E,0EAA0E;AAC1E,MAAM,2BAA2B,GAAG,IAAI,CAAC;AAEzC,kDAAkD;AAClD,MAAM,UAAU,gBAAgB,CAAC,OAAe,EAAE,MAAqB;IACrE,QAAQ,MAAM,EAAE,CAAC;QACf,KAAK,OAAO;YACV,mEAAmE;YACnE,OAAO,aAAa,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,2BAA2B,CAAC,CAAC;QACtE,KAAK,MAAM;YACT,kCAAkC;YAClC,OAAO,OAAO,CAAC;QACjB,KAAK,YAAY;YACf,6CAA6C;YAC7C,OAAO,OAAO,CAAC;QACjB;YACE,OAAO,OAAO,CAAC;IACnB,CAAC;AACH,CAAC;AAED,8DAA8D;AAC9D,SAAS,aAAa,CAAC,IAAY;IACjC,OAAO,IAAI;SACR,OAAO,CAAC,iBAAiB,EAAE,cAAc,CAAC,CAAC,cAAc;SACzD,OAAO,CAAC,YAAY,EAAE,IAAI,CAAC,CAAC,cAAc;SAC1C,OAAO,CAAC,kBAAkB,EAAE,IAAI,CAAC,CAAC,OAAO;SACzC,OAAO,CAAC,cAAc,EAAE,IAAI,CAAC,CAAC,SAAS;SACvC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,UAAU;SAC/B,OAAO,CAAC,wBAAwB,EAAE,IAAI,CAAC,CAAC,QAAQ;SAChD,OAAO,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC,CAAC,aAAa;AAC9C,CAAC"}
@@ -0,0 +1,32 @@
1
+ import type { Channel, IncomingMessage, OutgoingMessage, EngineEvent, MessageFormat, Modality } from "@kilnai/core";
2
+ export interface SlackConfig {
3
+ readonly botToken: string;
4
+ readonly signingSecret: string;
5
+ }
6
+ /**
7
+ * Channel adapter for Slack Bot Events API + Web API.
8
+ * receive() accepts parsed event payloads from Slack Events API.
9
+ * send() posts messages via chat.postMessage with optional thread_ts.
10
+ * stream() posts each engine event as a message.
11
+ * verifyRequest() validates Slack request signatures using HMAC-SHA256.
12
+ */
13
+ export declare class SlackChannel implements Channel {
14
+ readonly name = "slack";
15
+ readonly defaultFormat: MessageFormat;
16
+ readonly supportedModalities: readonly Modality[];
17
+ private readonly config;
18
+ private messageHandler;
19
+ constructor(config: SlackConfig);
20
+ /** Register a handler for incoming messages (from parsed Slack event payloads) */
21
+ onMessage(handler: (message: IncomingMessage) => void): void;
22
+ receive(message: IncomingMessage): Promise<void>;
23
+ send(response: OutgoingMessage): Promise<void>;
24
+ stream(events: AsyncIterable<EngineEvent>): Promise<void>;
25
+ /**
26
+ * Verify a Slack request signature.
27
+ * Computes HMAC-SHA256 of "v0:{timestamp}:{body}" with the signing secret
28
+ * and compares it to the provided signature using a timing-safe comparison.
29
+ */
30
+ verifyRequest(timestamp: string, body: string, signature: string): boolean;
31
+ }
32
+ //# sourceMappingURL=slack-channel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slack-channel.d.ts","sourceRoot":"","sources":["../../src/channels/slack-channel.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,WAAW,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAKpH,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAChC;AAED;;;;;;GAMG;AACH,qBAAa,YAAa,YAAW,OAAO;IAC1C,QAAQ,CAAC,IAAI,WAAW;IACxB,QAAQ,CAAC,aAAa,EAAE,aAAa,CAAU;IAC/C,QAAQ,CAAC,mBAAmB,EAAE,SAAS,QAAQ,EAAE,CAA6B;IAE9E,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAc;IACrC,OAAO,CAAC,cAAc,CAAqD;gBAE/D,MAAM,EAAE,WAAW;IAI/B,kFAAkF;IAClF,SAAS,CAAC,OAAO,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,IAAI,GAAG,IAAI;IAItD,OAAO,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAMhD,IAAI,CAAC,QAAQ,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAuB9C,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,WAAW,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAU/D;;;;OAIG;IACH,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO;CAK3E"}
@@ -0,0 +1,71 @@
1
+ // SlackChannel: Slack Bot Events API + Web API adapter
2
+ // Uses Slack Web API (slack.com/api) via native fetch -- no SDK dependency
3
+ // Request signature verification uses shared HMAC-SHA256 utility
4
+ import { extractText, textParts } from "@kilnai/core";
5
+ import { formatForChannel } from "./message-formatter.js";
6
+ import { verifyHmacSha256 } from "../utils/hmac.js";
7
+ /**
8
+ * Channel adapter for Slack Bot Events API + Web API.
9
+ * receive() accepts parsed event payloads from Slack Events API.
10
+ * send() posts messages via chat.postMessage with optional thread_ts.
11
+ * stream() posts each engine event as a message.
12
+ * verifyRequest() validates Slack request signatures using HMAC-SHA256.
13
+ */
14
+ export class SlackChannel {
15
+ name = "slack";
16
+ defaultFormat = "full";
17
+ supportedModalities = ["text", "image", "file"];
18
+ config;
19
+ messageHandler = null;
20
+ constructor(config) {
21
+ this.config = config;
22
+ }
23
+ /** Register a handler for incoming messages (from parsed Slack event payloads) */
24
+ onMessage(handler) {
25
+ this.messageHandler = handler;
26
+ }
27
+ async receive(message) {
28
+ if (this.messageHandler) {
29
+ this.messageHandler(message);
30
+ }
31
+ }
32
+ async send(response) {
33
+ const text = extractText(response.parts);
34
+ const formatted = formatForChannel(text, response.format ?? this.defaultFormat);
35
+ const body = {
36
+ channel: response.target,
37
+ text: formatted,
38
+ };
39
+ if (response.threadId !== undefined) {
40
+ body.thread_ts = response.threadId;
41
+ }
42
+ await fetch("https://slack.com/api/chat.postMessage", {
43
+ method: "POST",
44
+ headers: {
45
+ "Content-Type": "application/json",
46
+ Authorization: `Bearer ${this.config.botToken}`,
47
+ },
48
+ body: JSON.stringify(body),
49
+ });
50
+ }
51
+ async stream(events) {
52
+ for await (const event of events) {
53
+ await this.send({
54
+ parts: textParts(`[${event.type}] ${JSON.stringify(event.payload)}`),
55
+ target: "stream",
56
+ format: this.defaultFormat,
57
+ });
58
+ }
59
+ }
60
+ /**
61
+ * Verify a Slack request signature.
62
+ * Computes HMAC-SHA256 of "v0:{timestamp}:{body}" with the signing secret
63
+ * and compares it to the provided signature using a timing-safe comparison.
64
+ */
65
+ verifyRequest(timestamp, body, signature) {
66
+ const payload = `v0:${timestamp}:${body}`;
67
+ const sig = signature.startsWith("v0=") ? signature.slice(3) : signature;
68
+ return verifyHmacSha256(this.config.signingSecret, payload, sig);
69
+ }
70
+ }
71
+ //# sourceMappingURL=slack-channel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slack-channel.js","sourceRoot":"","sources":["../../src/channels/slack-channel.ts"],"names":[],"mappings":"AAAA,uDAAuD;AACvD,2EAA2E;AAC3E,iEAAiE;AAGjE,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAOpD;;;;;;GAMG;AACH,MAAM,OAAO,YAAY;IACd,IAAI,GAAG,OAAO,CAAC;IACf,aAAa,GAAkB,MAAM,CAAC;IACtC,mBAAmB,GAAwB,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;IAE7D,MAAM,CAAc;IAC7B,cAAc,GAAgD,IAAI,CAAC;IAE3E,YAAY,MAAmB;QAC7B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;IAED,kFAAkF;IAClF,SAAS,CAAC,OAA2C;QACnD,IAAI,CAAC,cAAc,GAAG,OAAO,CAAC;IAChC,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,OAAwB;QACpC,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;YACxB,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC;QAC/B,CAAC;IACH,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,QAAyB;QAClC,MAAM,IAAI,GAAG,WAAW,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;QACzC,MAAM,SAAS,GAAG,gBAAgB,CAAC,IAAI,EAAE,QAAQ,CAAC,MAAM,IAAI,IAAI,CAAC,aAAa,CAAC,CAAC;QAEhF,MAAM,IAAI,GAA4B;YACpC,OAAO,EAAE,QAAQ,CAAC,MAAM;YACxB,IAAI,EAAE,SAAS;SAChB,CAAC;QAEF,IAAI,QAAQ,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;YACpC,IAAI,CAAC,SAAS,GAAG,QAAQ,CAAC,QAAQ,CAAC;QACrC,CAAC;QAED,MAAM,KAAK,CAAC,wCAAwC,EAAE;YACpD,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACP,cAAc,EAAE,kBAAkB;gBAClC,aAAa,EAAE,UAAU,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE;aAChD;YACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;SAC3B,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,MAAkC;QAC7C,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YACjC,MAAM,IAAI,CAAC,IAAI,CAAC;gBACd,KAAK,EAAE,SAAS,CAAC,IAAI,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;gBACpE,MAAM,EAAE,QAAQ;gBAChB,MAAM,EAAE,IAAI,CAAC,aAAa;aAC3B,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED;;;;OAIG;IACH,aAAa,CAAC,SAAiB,EAAE,IAAY,EAAE,SAAiB;QAC9D,MAAM,OAAO,GAAG,MAAM,SAAS,IAAI,IAAI,EAAE,CAAC;QAC1C,MAAM,GAAG,GAAG,SAAS,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QACzE,OAAO,gBAAgB,CAAC,IAAI,CAAC,MAAM,CAAC,aAAa,EAAE,OAAO,EAAE,GAAG,CAAC,CAAC;IACnE,CAAC;CACF"}
@@ -0,0 +1,27 @@
1
+ import type { MessageFormat } from "@kilnai/core";
2
+ /** Configuration for a channel adapter */
3
+ export interface ChannelConfig {
4
+ readonly name: string;
5
+ readonly defaultFormat: MessageFormat;
6
+ readonly maxMessageLength?: number;
7
+ readonly rateLimitPerMinute?: number;
8
+ }
9
+ /** Runtime status of a channel adapter */
10
+ export type ChannelStatus = "connected" | "disconnected" | "error";
11
+ /** Maps a platform-specific user ID to an engine user ID */
12
+ export interface IdentityMapping {
13
+ readonly platformUserId: string;
14
+ readonly channelName: string;
15
+ readonly engineUserId: string;
16
+ }
17
+ /** Resolves platform user identities to engine user IDs */
18
+ export interface IdentityResolver {
19
+ resolve(channelName: string, platformUserId: string): Promise<string | null>;
20
+ }
21
+ /** In-memory identity resolver for development and testing */
22
+ export declare class InMemoryIdentityResolver implements IdentityResolver {
23
+ private readonly mappings;
24
+ addMapping(channelName: string, platformUserId: string, engineUserId: string): void;
25
+ resolve(channelName: string, platformUserId: string): Promise<string | null>;
26
+ }
27
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/channels/types.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAElD,0CAA0C;AAC1C,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,aAAa,EAAE,aAAa,CAAC;IACtC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IACnC,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAC;CACtC;AAED,0CAA0C;AAC1C,MAAM,MAAM,aAAa,GAAG,WAAW,GAAG,cAAc,GAAG,OAAO,CAAC;AAEnE,4DAA4D;AAC5D,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC/B;AAED,2DAA2D;AAC3D,MAAM,WAAW,gBAAgB;IAC/B,OAAO,CAAC,WAAW,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;CAC9E;AAED,8DAA8D;AAC9D,qBAAa,wBAAyB,YAAW,gBAAgB;IAC/D,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA6B;IAEtD,UAAU,CAAC,WAAW,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,IAAI;IAI7E,OAAO,CAAC,WAAW,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;CAGnF"}
@@ -0,0 +1,12 @@
1
+ // Channel adapter types: configuration, status, and identity mapping
2
+ /** In-memory identity resolver for development and testing */
3
+ export class InMemoryIdentityResolver {
4
+ mappings = new Map();
5
+ addMapping(channelName, platformUserId, engineUserId) {
6
+ this.mappings.set(`${channelName}:${platformUserId}`, engineUserId);
7
+ }
8
+ async resolve(channelName, platformUserId) {
9
+ return this.mappings.get(`${channelName}:${platformUserId}`) ?? null;
10
+ }
11
+ }
12
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/channels/types.ts"],"names":[],"mappings":"AAAA,qEAAqE;AA2BrE,8DAA8D;AAC9D,MAAM,OAAO,wBAAwB;IAClB,QAAQ,GAAG,IAAI,GAAG,EAAkB,CAAC;IAEtD,UAAU,CAAC,WAAmB,EAAE,cAAsB,EAAE,YAAoB;QAC1E,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,WAAW,IAAI,cAAc,EAAE,EAAE,YAAY,CAAC,CAAC;IACtE,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,WAAmB,EAAE,cAAsB;QACvD,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,WAAW,IAAI,cAAc,EAAE,CAAC,IAAI,IAAI,CAAC;IACvE,CAAC;CACF"}
@@ -0,0 +1,37 @@
1
+ import type { Channel, IncomingMessage, OutgoingMessage, EngineEvent, MessageFormat, Modality } from "@kilnai/core";
2
+ /** Minimal WebSocket interface (compatible with Hono WSContext) */
3
+ export interface WebSocketLike {
4
+ send(data: string): void;
5
+ readonly readyState: number;
6
+ }
7
+ /**
8
+ * Channel adapter for WebSocket connections.
9
+ * Clients are tracked per sessionId for targeted delivery.
10
+ * send() -- delivers to the session matching response.userId, or all sessions if absent.
11
+ * stream() -- always broadcasts to all sessions (engine events are global).
12
+ * receive() -- delegates to the registered onMessage handler.
13
+ */
14
+ export declare class WebChannel implements Channel {
15
+ readonly name = "web";
16
+ readonly defaultFormat: MessageFormat;
17
+ readonly supportedModalities: readonly Modality[];
18
+ private readonly sessions;
19
+ private messageHandler;
20
+ /** Register a handler for incoming WebSocket messages */
21
+ onMessage(handler: (message: IncomingMessage) => void): void;
22
+ /** Add a WebSocket client to the given session */
23
+ addClient(ws: WebSocketLike, sessionId: string): void;
24
+ /** Remove a WebSocket client from whichever session contains it */
25
+ removeClient(ws: WebSocketLike): void;
26
+ /** Total number of connected clients across all sessions */
27
+ get clientCount(): number;
28
+ receive(message: IncomingMessage): Promise<void>;
29
+ send(response: OutgoingMessage): Promise<void>;
30
+ stream(events: AsyncIterable<EngineEvent>): Promise<void>;
31
+ /** Send to clients in a specific session */
32
+ private sendToSession;
33
+ /** Send to all clients across all sessions */
34
+ private broadcastAll;
35
+ private trySend;
36
+ }
37
+ //# sourceMappingURL=web-channel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"web-channel.d.ts","sourceRoot":"","sources":["../../src/channels/web-channel.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,WAAW,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAIpH,mEAAmE;AACnE,MAAM,WAAW,aAAa;IAC5B,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED;;;;;;GAMG;AACH,qBAAa,UAAW,YAAW,OAAO;IACxC,QAAQ,CAAC,IAAI,SAAS;IACtB,QAAQ,CAAC,aAAa,EAAE,aAAa,CAAU;IAC/C,QAAQ,CAAC,mBAAmB,EAAE,SAAS,QAAQ,EAAE,CAAsC;IAEvF,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAyC;IAClE,OAAO,CAAC,cAAc,CAAqD;IAE3E,yDAAyD;IACzD,SAAS,CAAC,OAAO,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,IAAI,GAAG,IAAI;IAI5D,kDAAkD;IAClD,SAAS,CAAC,EAAE,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI;IASrD,mEAAmE;IACnE,YAAY,CAAC,EAAE,EAAE,aAAa,GAAG,IAAI;IAWrC,4DAA4D;IAC5D,IAAI,WAAW,IAAI,MAAM,CAMxB;IAEK,OAAO,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAMhD,IAAI,CAAC,QAAQ,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAmB9C,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,WAAW,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAY/D,4CAA4C;IAC5C,OAAO,CAAC,aAAa;IAQrB,8CAA8C;IAC9C,OAAO,CAAC,YAAY;IAQpB,OAAO,CAAC,OAAO;CAShB"}
@@ -0,0 +1,112 @@
1
+ // WebChannel: wraps Hono WebSocket connections as a Channel adapter
2
+ // Session-scoped delivery: send() targets a session by userId, stream() is global.
3
+ import { extractText } from "@kilnai/core";
4
+ import { formatForChannel } from "./message-formatter.js";
5
+ /**
6
+ * Channel adapter for WebSocket connections.
7
+ * Clients are tracked per sessionId for targeted delivery.
8
+ * send() -- delivers to the session matching response.userId, or all sessions if absent.
9
+ * stream() -- always broadcasts to all sessions (engine events are global).
10
+ * receive() -- delegates to the registered onMessage handler.
11
+ */
12
+ export class WebChannel {
13
+ name = "web";
14
+ defaultFormat = "full";
15
+ supportedModalities = ["text", "image", "audio", "file"];
16
+ sessions = new Map();
17
+ messageHandler = null;
18
+ /** Register a handler for incoming WebSocket messages */
19
+ onMessage(handler) {
20
+ this.messageHandler = handler;
21
+ }
22
+ /** Add a WebSocket client to the given session */
23
+ addClient(ws, sessionId) {
24
+ let set = this.sessions.get(sessionId);
25
+ if (!set) {
26
+ set = new Set();
27
+ this.sessions.set(sessionId, set);
28
+ }
29
+ set.add(ws);
30
+ }
31
+ /** Remove a WebSocket client from whichever session contains it */
32
+ removeClient(ws) {
33
+ for (const [sessionId, set] of this.sessions) {
34
+ if (set.delete(ws)) {
35
+ if (set.size === 0) {
36
+ this.sessions.delete(sessionId);
37
+ }
38
+ return;
39
+ }
40
+ }
41
+ }
42
+ /** Total number of connected clients across all sessions */
43
+ get clientCount() {
44
+ let total = 0;
45
+ for (const set of this.sessions.values()) {
46
+ total += set.size;
47
+ }
48
+ return total;
49
+ }
50
+ async receive(message) {
51
+ if (this.messageHandler) {
52
+ this.messageHandler(message);
53
+ }
54
+ }
55
+ async send(response) {
56
+ const text = extractText(response.parts);
57
+ const formatted = formatForChannel(text, response.format ?? this.defaultFormat);
58
+ const payload = JSON.stringify({
59
+ type: "output",
60
+ text: formatted,
61
+ parts: response.parts,
62
+ target: response.target,
63
+ userId: response.userId,
64
+ threadId: response.threadId,
65
+ });
66
+ if (response.userId) {
67
+ this.sendToSession(response.userId, payload);
68
+ }
69
+ else {
70
+ this.broadcastAll(payload);
71
+ }
72
+ }
73
+ async stream(events) {
74
+ for await (const event of events) {
75
+ const payload = JSON.stringify({
76
+ type: "event",
77
+ event: event.type,
78
+ data: event.payload,
79
+ timestamp: event.timestamp,
80
+ });
81
+ this.broadcastAll(payload);
82
+ }
83
+ }
84
+ /** Send to clients in a specific session */
85
+ sendToSession(sessionId, payload) {
86
+ const set = this.sessions.get(sessionId);
87
+ if (!set)
88
+ return;
89
+ for (const client of set) {
90
+ this.trySend(set, client, payload);
91
+ }
92
+ }
93
+ /** Send to all clients across all sessions */
94
+ broadcastAll(payload) {
95
+ for (const set of this.sessions.values()) {
96
+ for (const client of set) {
97
+ this.trySend(set, client, payload);
98
+ }
99
+ }
100
+ }
101
+ trySend(set, client, payload) {
102
+ try {
103
+ if (client.readyState === 1) {
104
+ client.send(payload);
105
+ }
106
+ }
107
+ catch {
108
+ set.delete(client);
109
+ }
110
+ }
111
+ }
112
+ //# sourceMappingURL=web-channel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"web-channel.js","sourceRoot":"","sources":["../../src/channels/web-channel.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ /** WhatsApp Cloud API version */
2
+ export declare const WHATSAPP_GRAPH_API_VERSION = "v21.0";
3
+ /** Build the WhatsApp Cloud API messages endpoint URL */
4
+ export declare function whatsappMessagesUrl(phoneNumberId: string): string;
5
+ /**
6
+ * Build the Graph API URL for retrieving WhatsApp media by ID.
7
+ * The caller must include a valid access token in the Authorization header to fetch the actual binary.
8
+ */
9
+ export declare function whatsappMediaUrl(mediaId: string): string;
10
+ /**
11
+ * Send a message via the WhatsApp Cloud API.
12
+ *
13
+ * @param phoneNumberId - The WhatsApp Business phone number ID
14
+ * @param accessToken - The access token for authentication
15
+ * @param to - The recipient phone number
16
+ * @param body - The message payload (type, text, image, audio, document, etc.)
17
+ * @returns The fetch Response
18
+ */
19
+ export declare function sendWhatsAppMessage(phoneNumberId: string, accessToken: string, to: string, body: Record<string, unknown>): Promise<Response>;
20
+ //# sourceMappingURL=whatsapp-api.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"whatsapp-api.d.ts","sourceRoot":"","sources":["../../src/channels/whatsapp-api.ts"],"names":[],"mappings":"AAGA,iCAAiC;AACjC,eAAO,MAAM,0BAA0B,UAAU,CAAC;AAElD,yDAAyD;AACzD,wBAAgB,mBAAmB,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAEjE;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAExD;AAED;;;;;;;;GAQG;AACH,wBAAsB,mBAAmB,CACvC,aAAa,EAAE,MAAM,EACrB,WAAW,EAAE,MAAM,EACnB,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,OAAO,CAAC,QAAQ,CAAC,CAkBnB"}
@@ -0,0 +1,44 @@
1
+ // Shared WhatsApp Cloud API client
2
+ // Single source of truth for graph.facebook.com URL construction, headers, and POST logic
3
+ /** WhatsApp Cloud API version */
4
+ export const WHATSAPP_GRAPH_API_VERSION = "v21.0";
5
+ /** Build the WhatsApp Cloud API messages endpoint URL */
6
+ export function whatsappMessagesUrl(phoneNumberId) {
7
+ return `https://graph.facebook.com/${WHATSAPP_GRAPH_API_VERSION}/${phoneNumberId}/messages`;
8
+ }
9
+ /**
10
+ * Build the Graph API URL for retrieving WhatsApp media by ID.
11
+ * The caller must include a valid access token in the Authorization header to fetch the actual binary.
12
+ */
13
+ export function whatsappMediaUrl(mediaId) {
14
+ return `https://graph.facebook.com/${WHATSAPP_GRAPH_API_VERSION}/${mediaId}`;
15
+ }
16
+ /**
17
+ * Send a message via the WhatsApp Cloud API.
18
+ *
19
+ * @param phoneNumberId - The WhatsApp Business phone number ID
20
+ * @param accessToken - The access token for authentication
21
+ * @param to - The recipient phone number
22
+ * @param body - The message payload (type, text, image, audio, document, etc.)
23
+ * @returns The fetch Response
24
+ */
25
+ export async function sendWhatsAppMessage(phoneNumberId, accessToken, to, body) {
26
+ const res = await fetch(whatsappMessagesUrl(phoneNumberId), {
27
+ method: "POST",
28
+ headers: {
29
+ "Content-Type": "application/json",
30
+ Authorization: `Bearer ${accessToken}`,
31
+ },
32
+ body: JSON.stringify({
33
+ messaging_product: "whatsapp",
34
+ to,
35
+ ...body,
36
+ }),
37
+ });
38
+ if (!res.ok) {
39
+ const text = await res.text().catch(() => "(unreadable)");
40
+ throw new Error(`WhatsApp API error ${res.status}: ${text}`);
41
+ }
42
+ return res;
43
+ }
44
+ //# sourceMappingURL=whatsapp-api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"whatsapp-api.js","sourceRoot":"","sources":["../../src/channels/whatsapp-api.ts"],"names":[],"mappings":"AAAA,mCAAmC;AACnC,0FAA0F;AAE1F,iCAAiC;AACjC,MAAM,CAAC,MAAM,0BAA0B,GAAG,OAAO,CAAC;AAElD,yDAAyD;AACzD,MAAM,UAAU,mBAAmB,CAAC,aAAqB;IACvD,OAAO,8BAA8B,0BAA0B,IAAI,aAAa,WAAW,CAAC;AAC9F,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,OAAe;IAC9C,OAAO,8BAA8B,0BAA0B,IAAI,OAAO,EAAE,CAAC;AAC/E,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,aAAqB,EACrB,WAAmB,EACnB,EAAU,EACV,IAA6B;IAE7B,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,mBAAmB,CAAC,aAAa,CAAC,EAAE;QAC1D,MAAM,EAAE,MAAM;QACd,OAAO,EAAE;YACP,cAAc,EAAE,kBAAkB;YAClC,aAAa,EAAE,UAAU,WAAW,EAAE;SACvC;QACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;YACnB,iBAAiB,EAAE,UAAU;YAC7B,EAAE;YACF,GAAG,IAAI;SACR,CAAC;KACH,CAAC,CAAC;IACH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACZ,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,cAAc,CAAC,CAAC;QAC1D,MAAM,IAAI,KAAK,CAAC,sBAAsB,GAAG,CAAC,MAAM,KAAK,IAAI,EAAE,CAAC,CAAC;IAC/D,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -0,0 +1,32 @@
1
+ import type { Channel, IncomingMessage, OutgoingMessage, EngineEvent, MessageFormat, Modality } from "@kilnai/core";
2
+ export interface WhatsAppConfig {
3
+ readonly phoneNumberId: string;
4
+ readonly accessToken: string;
5
+ readonly verifyToken: string;
6
+ }
7
+ /**
8
+ * Channel adapter for WhatsApp Business API.
9
+ * receive() accepts parsed webhook messages from WhatsApp Cloud API.
10
+ * send() posts text messages via graph.facebook.com.
11
+ * stream() sends each engine event as a summarized text message.
12
+ * verifyWebhook() handles the one-time webhook verification handshake.
13
+ */
14
+ export declare class WhatsAppChannel implements Channel {
15
+ readonly name = "whatsapp";
16
+ readonly defaultFormat: MessageFormat;
17
+ readonly supportedModalities: readonly Modality[];
18
+ private readonly config;
19
+ private messageHandler;
20
+ constructor(config: WhatsAppConfig);
21
+ /** Register a handler for incoming messages (from parsed webhook payloads) */
22
+ onMessage(handler: (message: IncomingMessage) => void): void;
23
+ receive(message: IncomingMessage): Promise<void>;
24
+ send(response: OutgoingMessage): Promise<void>;
25
+ stream(events: AsyncIterable<EngineEvent>): Promise<void>;
26
+ /**
27
+ * Verify a WhatsApp webhook subscription request.
28
+ * Returns the challenge string if mode is "subscribe" and token matches, else null.
29
+ */
30
+ verifyWebhook(mode: string, token: string, challenge: string): string | null;
31
+ }
32
+ //# sourceMappingURL=whatsapp-channel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"whatsapp-channel.d.ts","sourceRoot":"","sources":["../../src/channels/whatsapp-channel.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,WAAW,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAKpH,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B;AAED;;;;;;GAMG;AACH,qBAAa,eAAgB,YAAW,OAAO;IAC7C,QAAQ,CAAC,IAAI,cAAc;IAC3B,QAAQ,CAAC,aAAa,EAAE,aAAa,CAAW;IAChD,QAAQ,CAAC,mBAAmB,EAAE,SAAS,QAAQ,EAAE,CAAsC;IAEvF,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAiB;IACxC,OAAO,CAAC,cAAc,CAAqD;gBAE/D,MAAM,EAAE,cAAc;IAIlC,8EAA8E;IAC9E,SAAS,CAAC,OAAO,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,IAAI,GAAG,IAAI;IAItD,OAAO,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAMhD,IAAI,CAAC,QAAQ,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAmC9C,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,WAAW,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAU/D;;;OAGG;IACH,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;CAM7E"}