@rk0429/agentic-relay 21.3.0 → 22.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. package/dist/application/chat/chat-daemon-service.d.ts +88 -0
  2. package/dist/application/chat/chat-daemon-service.js +358 -0
  3. package/dist/application/chat/chat-daemon-service.js.map +1 -0
  4. package/dist/application/chat/chat-inbound-handler.d.ts +35 -0
  5. package/dist/application/chat/chat-inbound-handler.js +193 -0
  6. package/dist/application/chat/chat-inbound-handler.js.map +1 -0
  7. package/dist/application/chat/chat-logging.d.ts +8 -0
  8. package/dist/application/chat/chat-logging.js +16 -0
  9. package/dist/application/chat/chat-logging.js.map +1 -0
  10. package/dist/application/chat/chat-outbound-handler.d.ts +17 -0
  11. package/dist/application/chat/chat-outbound-handler.js +207 -0
  12. package/dist/application/chat/chat-outbound-handler.js.map +1 -0
  13. package/dist/application/chat/chat-question-service.d.ts +37 -0
  14. package/dist/application/chat/chat-question-service.js +135 -0
  15. package/dist/application/chat/chat-question-service.js.map +1 -0
  16. package/dist/application/chat/chat-setup-service.d.ts +27 -0
  17. package/dist/application/chat/chat-setup-service.js +183 -0
  18. package/dist/application/chat/chat-setup-service.js.map +1 -0
  19. package/dist/application/chat/message-queue-manager.d.ts +13 -0
  20. package/dist/application/chat/message-queue-manager.js +34 -0
  21. package/dist/application/chat/message-queue-manager.js.map +1 -0
  22. package/dist/application/housekeeping.js +6 -2
  23. package/dist/application/housekeeping.js.map +1 -1
  24. package/dist/application/routine-status-query-service.d.ts +17 -0
  25. package/dist/application/routine-status-query-service.js +111 -0
  26. package/dist/application/routine-status-query-service.js.map +1 -1
  27. package/dist/bin/relay.d.ts +4 -2
  28. package/dist/bin/relay.js +301 -12
  29. package/dist/bin/relay.js.map +1 -1
  30. package/dist/core/types.d.ts +8 -0
  31. package/dist/core/types.js +1 -0
  32. package/dist/core/types.js.map +1 -1
  33. package/dist/domain/chat/message-cleaner.d.ts +11 -0
  34. package/dist/domain/chat/message-cleaner.js +54 -0
  35. package/dist/domain/chat/message-cleaner.js.map +1 -0
  36. package/dist/domain/chat/message-router.d.ts +8 -0
  37. package/dist/domain/chat/message-router.js +97 -0
  38. package/dist/domain/chat/message-router.js.map +1 -0
  39. package/dist/domain/chat/message-splitter.d.ts +3 -0
  40. package/dist/domain/chat/message-splitter.js +148 -0
  41. package/dist/domain/chat/message-splitter.js.map +1 -0
  42. package/dist/domain/chat/platform-connection.d.ts +31 -0
  43. package/dist/domain/chat/platform-connection.js +67 -0
  44. package/dist/domain/chat/platform-connection.js.map +1 -0
  45. package/dist/domain/chat/platform-types.d.ts +18 -0
  46. package/dist/domain/chat/platform-types.js +14 -0
  47. package/dist/domain/chat/platform-types.js.map +1 -0
  48. package/dist/domain/chat/ports.d.ts +31 -0
  49. package/dist/domain/chat/ports.js +2 -0
  50. package/dist/domain/chat/ports.js.map +1 -0
  51. package/dist/domain/chat/session-bridge.d.ts +41 -0
  52. package/dist/domain/chat/session-bridge.js +93 -0
  53. package/dist/domain/chat/session-bridge.js.map +1 -0
  54. package/dist/domain/chat/types.d.ts +85 -0
  55. package/dist/domain/chat/types.js +2 -0
  56. package/dist/domain/chat/types.js.map +1 -0
  57. package/dist/domain/chat/user-question.d.ts +33 -0
  58. package/dist/domain/chat/user-question.js +73 -0
  59. package/dist/domain/chat/user-question.js.map +1 -0
  60. package/dist/domain/config.d.ts +3 -1
  61. package/dist/domain/config.js +9 -0
  62. package/dist/domain/config.js.map +1 -1
  63. package/dist/domain/routine-loader.d.ts +2 -0
  64. package/dist/domain/routine-loader.js +3 -0
  65. package/dist/domain/routine-loader.js.map +1 -1
  66. package/dist/infrastructure/chat/adapter-factory.d.ts +5 -0
  67. package/dist/infrastructure/chat/adapter-factory.js +15 -0
  68. package/dist/infrastructure/chat/adapter-factory.js.map +1 -0
  69. package/dist/infrastructure/chat/adapters/discord-adapter.d.ts +23 -0
  70. package/dist/infrastructure/chat/adapters/discord-adapter.js +199 -0
  71. package/dist/infrastructure/chat/adapters/discord-adapter.js.map +1 -0
  72. package/dist/infrastructure/chat/adapters/slack-adapter.d.ts +24 -0
  73. package/dist/infrastructure/chat/adapters/slack-adapter.js +155 -0
  74. package/dist/infrastructure/chat/adapters/slack-adapter.js.map +1 -0
  75. package/dist/infrastructure/chat/agent-gateway-impl.d.ts +21 -0
  76. package/dist/infrastructure/chat/agent-gateway-impl.js +101 -0
  77. package/dist/infrastructure/chat/agent-gateway-impl.js.map +1 -0
  78. package/dist/infrastructure/chat/chat-ipc-client.d.ts +33 -0
  79. package/dist/infrastructure/chat/chat-ipc-client.js +70 -0
  80. package/dist/infrastructure/chat/chat-ipc-client.js.map +1 -0
  81. package/dist/infrastructure/chat/chat-ipc-server.d.ts +19 -0
  82. package/dist/infrastructure/chat/chat-ipc-server.js +125 -0
  83. package/dist/infrastructure/chat/chat-ipc-server.js.map +1 -0
  84. package/dist/infrastructure/chat/chat-logger.d.ts +24 -0
  85. package/dist/infrastructure/chat/chat-logger.js +86 -0
  86. package/dist/infrastructure/chat/chat-logger.js.map +1 -0
  87. package/dist/infrastructure/chat/credentials-repository.d.ts +8 -0
  88. package/dist/infrastructure/chat/credentials-repository.js +84 -0
  89. package/dist/infrastructure/chat/credentials-repository.js.map +1 -0
  90. package/dist/infrastructure/chat/session-bridge-repository.d.ts +13 -0
  91. package/dist/infrastructure/chat/session-bridge-repository.js +120 -0
  92. package/dist/infrastructure/chat/session-bridge-repository.js.map +1 -0
  93. package/dist/infrastructure/chat/token-masking.d.ts +3 -0
  94. package/dist/infrastructure/chat/token-masking.js +22 -0
  95. package/dist/infrastructure/chat/token-masking.js.map +1 -0
  96. package/dist/infrastructure/store/relay-store.js +3 -1
  97. package/dist/infrastructure/store/relay-store.js.map +1 -1
  98. package/dist/interfaces/cli/chat-cli.d.ts +34 -0
  99. package/dist/interfaces/cli/chat-cli.js +68 -0
  100. package/dist/interfaces/cli/chat-cli.js.map +1 -0
  101. package/dist/interfaces/cli/relay-cli-args.d.ts +16 -1
  102. package/dist/interfaces/cli/relay-cli-args.js +135 -2
  103. package/dist/interfaces/cli/relay-cli-args.js.map +1 -1
  104. package/dist/interfaces/mcp/chat-tools.d.ts +34 -0
  105. package/dist/interfaces/mcp/chat-tools.js +131 -0
  106. package/dist/interfaces/mcp/chat-tools.js.map +1 -0
  107. package/dist/interfaces/mcp/relay-mcp-server.js +43 -6
  108. package/dist/interfaces/mcp/relay-mcp-server.js.map +1 -1
  109. package/package.json +5 -1
  110. package/scripts/check-chat-boundary.mjs +220 -0
  111. package/scripts/check-chat-boundary.test.ts +139 -0
  112. package/scripts/check-circular-deps.mjs +199 -0
  113. package/scripts/check-circular-deps.test.ts +62 -0
@@ -0,0 +1,193 @@
1
+ import { emitChatLog } from "./chat-logging.js";
2
+ import { MessageRouter } from "../../domain/chat/message-router.js";
3
+ import { SessionBridge, ThreadKey } from "../../domain/chat/session-bridge.js";
4
+ export class ChatInboundHandler {
5
+ deps;
6
+ activeOperations = 0;
7
+ idleWaiters = new Set();
8
+ sessionTimeoutMinutes;
9
+ maxMessageLength;
10
+ logger;
11
+ constructor(deps) {
12
+ this.deps = deps;
13
+ this.sessionTimeoutMinutes = deps.sessionTimeoutMinutes ?? 30;
14
+ this.maxMessageLength = deps.maxMessageLength ?? 10_000;
15
+ this.logger = deps.logger;
16
+ }
17
+ configure(options) {
18
+ if (options.sessionTimeoutMinutes !== undefined) {
19
+ this.sessionTimeoutMinutes = Math.max(1, Math.floor(options.sessionTimeoutMinutes));
20
+ }
21
+ if (options.maxMessageLength !== undefined) {
22
+ this.maxMessageLength = Math.max(1, Math.floor(options.maxMessageLength));
23
+ }
24
+ if (options.messageQueueMaxSize !== undefined) {
25
+ this.deps.messageQueueManager.setMaxBufferedPerThread(options.messageQueueMaxSize);
26
+ }
27
+ if (options.logger !== undefined) {
28
+ this.logger = options.logger;
29
+ }
30
+ }
31
+ async handleInbound(event) {
32
+ const startTime = Date.now();
33
+ const durationMs = () => Date.now() - startTime;
34
+ const incomingKey = new ThreadKey({
35
+ platform: event.platform,
36
+ teamOrGuildId: event.teamOrGuildId,
37
+ channelId: event.channelId,
38
+ threadId: event.threadId,
39
+ });
40
+ const queueKey = incomingKey.toString();
41
+ await this.log("info", "chat inbound received", {
42
+ platform: event.platform,
43
+ threadKey: queueKey,
44
+ teamOrGuildId: event.teamOrGuildId,
45
+ channelId: event.channelId,
46
+ threadId: event.threadId,
47
+ messageId: event.messageId,
48
+ userId: event.userId,
49
+ isDM: event.isDM,
50
+ isMention: event.isMention,
51
+ });
52
+ if (this.deps.messageQueueManager.isProcessing(queueKey)) {
53
+ this.deps.messageQueueManager.enqueue(queueKey, event);
54
+ await this.log("warn", "chat inbound buffered", {
55
+ platform: event.platform,
56
+ threadKey: queueKey,
57
+ messageId: event.messageId,
58
+ durationMs: durationMs(),
59
+ });
60
+ return;
61
+ }
62
+ const intent = MessageRouter.determineIntent(event, (key) => this.deps.sessionBridgeRepository.findByThreadKey(key), (key) => this.deps.questionService.findPendingByThreadKey(key), this.sessionTimeoutMinutes, this.maxMessageLength);
63
+ if (intent.kind === "ignored") {
64
+ await this.log("info", "chat inbound ignored", {
65
+ platform: event.platform,
66
+ reason: intent.reason,
67
+ threadKey: queueKey,
68
+ intent: intent.kind,
69
+ durationMs: durationMs(),
70
+ });
71
+ return;
72
+ }
73
+ this.activeOperations += 1;
74
+ this.deps.messageQueueManager.startProcessing(queueKey);
75
+ const stopIndicator = await this.deps.outboundHandler.beginProcessingIndicator(event);
76
+ try {
77
+ if (intent.kind === "answer_question") {
78
+ await this.deps.questionService.handlePotentialAnswer(event);
79
+ await this.log("info", "chat answer processed", {
80
+ platform: event.platform,
81
+ threadKey: queueKey,
82
+ intent: intent.kind,
83
+ durationMs: durationMs(),
84
+ });
85
+ return;
86
+ }
87
+ if (intent.kind === "new_session") {
88
+ await this.handleNewSession(event, intent, durationMs);
89
+ return;
90
+ }
91
+ await this.handleContinuation(event, intent, durationMs);
92
+ }
93
+ finally {
94
+ await stopIndicator?.();
95
+ this.deps.messageQueueManager.finishProcessing(queueKey);
96
+ this.activeOperations = Math.max(0, this.activeOperations - 1);
97
+ if (this.activeOperations === 0) {
98
+ for (const resolve of this.idleWaiters) {
99
+ resolve();
100
+ }
101
+ this.idleWaiters.clear();
102
+ }
103
+ for (const buffered of this.deps.messageQueueManager.dequeueAll(queueKey)) {
104
+ await this.handleInbound(buffered);
105
+ }
106
+ }
107
+ }
108
+ async waitForIdle(timeoutMs) {
109
+ if (this.activeOperations === 0) {
110
+ return;
111
+ }
112
+ await Promise.race([
113
+ new Promise((resolve) => this.idleWaiters.add(resolve)),
114
+ new Promise((resolve) => setTimeout(resolve, timeoutMs)),
115
+ ]);
116
+ }
117
+ async handleNewSession(event, intent, durationMs) {
118
+ let responseTarget = {
119
+ channelId: event.channelId,
120
+ threadId: event.threadId,
121
+ userId: event.isDM ? event.userId : undefined,
122
+ messageId: event.messageId,
123
+ };
124
+ if (intent.invalidBackendWarning) {
125
+ const warningResult = await this.deps.outboundHandler.sendResponse(event.platform, responseTarget, `⚠️ Unknown backend "${intent.invalidBackendWarning}". Using default routing.`);
126
+ responseTarget = {
127
+ ...responseTarget,
128
+ channelId: warningResult.channelId ?? responseTarget.channelId,
129
+ threadId: warningResult.threadId ?? responseTarget.threadId,
130
+ messageId: warningResult.threadId ?? responseTarget.threadId
131
+ ? undefined
132
+ : responseTarget.messageId,
133
+ };
134
+ }
135
+ const result = await this.deps.agentGateway.startSession(intent.cleanedText, intent.backendSpec);
136
+ const sendResult = await this.deps.outboundHandler.sendResponse(event.platform, responseTarget, result.response);
137
+ const bridge = SessionBridge.create(new ThreadKey({
138
+ platform: event.platform,
139
+ teamOrGuildId: sendResult.teamOrGuildId ?? event.teamOrGuildId,
140
+ channelId: sendResult.channelId ?? event.channelId,
141
+ threadId: sendResult.threadId ?? event.threadId,
142
+ }), result.relaySessionId, result.backend);
143
+ bridge.backendSessionId = result.backendSessionId ?? bridge.backendSessionId;
144
+ this.deps.sessionBridgeRepository.save(bridge);
145
+ await this.log("info", "chat session started", {
146
+ platform: event.platform,
147
+ relaySessionId: result.relaySessionId,
148
+ backend: result.backend,
149
+ threadKey: bridge.threadKey.toString(),
150
+ intent: intent.kind,
151
+ durationMs: durationMs(),
152
+ });
153
+ }
154
+ async handleContinuation(event, intent, durationMs) {
155
+ intent.bridge.startProcessing();
156
+ this.deps.sessionBridgeRepository.save(intent.bridge);
157
+ const result = await this.deps.agentGateway.continueSession(intent.bridge.relaySessionId, intent.cleanedText);
158
+ const sendResult = await this.deps.outboundHandler.sendResponse(event.platform, {
159
+ channelId: intent.bridge.threadKey.channelId,
160
+ threadId: intent.bridge.threadKey.threadId,
161
+ userId: event.isDM ? event.userId : undefined,
162
+ messageId: event.messageId,
163
+ }, result.response);
164
+ const updatedBridge = new SessionBridge({
165
+ threadKey: new ThreadKey({
166
+ platform: event.platform,
167
+ teamOrGuildId: sendResult.teamOrGuildId ?? intent.bridge.threadKey.teamOrGuildId,
168
+ channelId: sendResult.channelId ?? intent.bridge.threadKey.channelId,
169
+ threadId: sendResult.threadId ?? intent.bridge.threadKey.threadId,
170
+ }),
171
+ relaySessionId: result.relaySessionId,
172
+ backendSessionId: result.backendSessionId ?? intent.bridge.backendSessionId,
173
+ backend: result.backend,
174
+ createdAt: intent.bridge.createdAt,
175
+ lastActivityAt: new Date(),
176
+ isProcessing: false,
177
+ });
178
+ updatedBridge.finishProcessing();
179
+ this.deps.sessionBridgeRepository.save(updatedBridge);
180
+ await this.log("info", "chat session continued", {
181
+ platform: event.platform,
182
+ relaySessionId: result.relaySessionId,
183
+ backend: result.backend,
184
+ threadKey: updatedBridge.threadKey.toString(),
185
+ intent: intent.kind,
186
+ durationMs: durationMs(),
187
+ });
188
+ }
189
+ async log(level, message, metadata) {
190
+ await emitChatLog(this.logger, level, message, metadata);
191
+ }
192
+ }
193
+ //# sourceMappingURL=chat-inbound-handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chat-inbound-handler.js","sourceRoot":"","sources":["../../../src/application/chat/chat-inbound-handler.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ export interface ChatStructuredLogger {
2
+ info(message: string, metadata?: Record<string, unknown>): Promise<void> | void;
3
+ warn(message: string, metadata?: Record<string, unknown>): Promise<void> | void;
4
+ error(message: string, metadata?: Record<string, unknown>): Promise<void> | void;
5
+ }
6
+ export type ChatLoggerLike = ChatStructuredLogger | ((message: string, metadata?: Record<string, unknown>) => Promise<void> | void);
7
+ export declare const noopChatLogger: ChatStructuredLogger;
8
+ export declare function emitChatLog(logger: ChatLoggerLike | undefined, level: keyof ChatStructuredLogger, message: string, metadata?: Record<string, unknown>): Promise<void>;
@@ -0,0 +1,16 @@
1
+ export const noopChatLogger = {
2
+ info() { },
3
+ warn() { },
4
+ error() { },
5
+ };
6
+ export async function emitChatLog(logger, level, message, metadata) {
7
+ if (!logger) {
8
+ return;
9
+ }
10
+ if (typeof logger === "function") {
11
+ await Promise.resolve(logger(message, metadata));
12
+ return;
13
+ }
14
+ await Promise.resolve(logger[level](message, metadata));
15
+ }
16
+ //# sourceMappingURL=chat-logging.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chat-logging.js","sourceRoot":"","sources":["../../../src/application/chat/chat-logging.ts"],"names":[],"mappings":"AAUA,MAAM,CAAC,MAAM,cAAc,GAAyB;IAClD,IAAI,KAAU,CAAC;IACf,IAAI,KAAU,CAAC;IACf,KAAK,KAAU,CAAC;CACjB,CAAC;AAEF,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,MAAkC,EAClC,KAAiC,EACjC,OAAe,EACf,QAAkC;IAElC,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO;IACT,CAAC;IAED,IAAI,OAAO,MAAM,KAAK,UAAU,EAAE,CAAC;QACjC,MAAM,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC;QACjD,OAAO;IACT,CAAC;IAED,MAAM,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC;AAC1D,CAAC"}
@@ -0,0 +1,17 @@
1
+ import { PlatformType, type ChatChannel } from "../../domain/chat/platform-types.js";
2
+ import type { ChatPlatformAdapter } from "../../domain/chat/ports.js";
3
+ import type { ChatTarget, InboundChatEvent, SendResult } from "../../domain/chat/types.js";
4
+ export declare class ChatOutboundHandler {
5
+ private adaptersByPlatform;
6
+ constructor(adapters?: ChatPlatformAdapter[]);
7
+ setAdapters(adapters: ChatPlatformAdapter[]): void;
8
+ sendResponse(platform: PlatformType | undefined, target: Omit<ChatTarget, "platform">, content: string): Promise<SendResult>;
9
+ listChannels(platform?: PlatformType): Promise<ChatChannel[]>;
10
+ beginProcessingIndicator(event: InboundChatEvent): Promise<(() => Promise<void>) | undefined>;
11
+ static markdownToSlackMrkdwn(markdown: string): string;
12
+ static markdownToSlackBlocks(markdown: string): Array<Record<string, unknown>>;
13
+ static markdownToDiscordEmbed(markdown: string): Record<string, unknown> | null;
14
+ private resolveAdapter;
15
+ private resolveChannelName;
16
+ private buildMessage;
17
+ }
@@ -0,0 +1,207 @@
1
+ import { ValidationError } from "../../core/types.js";
2
+ import { PlatformType } from "../../domain/chat/platform-types.js";
3
+ import { MessageSplitter } from "../../domain/chat/message-splitter.js";
4
+ export class ChatOutboundHandler {
5
+ adaptersByPlatform;
6
+ constructor(adapters = []) {
7
+ this.adaptersByPlatform = new Map();
8
+ this.setAdapters(adapters);
9
+ }
10
+ setAdapters(adapters) {
11
+ this.adaptersByPlatform = new Map(adapters.map((adapter) => [adapter.platform, adapter]));
12
+ }
13
+ async sendResponse(platform, target, content) {
14
+ const adapter = this.resolveAdapter(platform);
15
+ const resolvedTarget = await this.resolveChannelName(adapter, target);
16
+ const maxLength = adapter.platform === PlatformType.Slack ? 4_000 : 2_000;
17
+ const pieces = MessageSplitter.split(content, maxLength);
18
+ const baseTarget = {
19
+ platform: adapter.platform,
20
+ ...resolvedTarget,
21
+ };
22
+ let replyTarget = baseTarget;
23
+ let firstResult;
24
+ for (const piece of pieces) {
25
+ const message = this.buildMessage(adapter.platform, piece);
26
+ const result = await adapter.sendMessage(replyTarget, message);
27
+ firstResult ??= result;
28
+ if (!replyTarget.threadId && result.threadId) {
29
+ replyTarget = {
30
+ ...replyTarget,
31
+ channelId: result.channelId ?? replyTarget.channelId,
32
+ threadId: result.threadId,
33
+ messageId: undefined,
34
+ };
35
+ }
36
+ }
37
+ return firstResult ?? {
38
+ success: true,
39
+ messageId: "",
40
+ channelId: resolvedTarget.channelId,
41
+ threadId: resolvedTarget.threadId,
42
+ };
43
+ }
44
+ async listChannels(platform) {
45
+ const adapter = this.resolveAdapter(platform);
46
+ return adapter.getChannels();
47
+ }
48
+ async beginProcessingIndicator(event) {
49
+ const adapter = this.adaptersByPlatform.get(event.platform);
50
+ const stopIndicator = await adapter?.beginProcessingIndicator?.(event);
51
+ return typeof stopIndicator === "function" ? stopIndicator : undefined;
52
+ }
53
+ static markdownToSlackMrkdwn(markdown) {
54
+ return markdown
55
+ .replace(/\r\n/g, "\n")
56
+ .replace(/^#\s+(.+)$/gm, (_, title) => `*${title.trim()}*`)
57
+ .replace(/\*\*(.+?)\*\*/g, "*$1*");
58
+ }
59
+ static markdownToSlackBlocks(markdown) {
60
+ const blocks = [];
61
+ const segments = splitMarkdownSegments(markdown);
62
+ for (const segment of segments) {
63
+ const trimmed = segment.trim();
64
+ if (!trimmed) {
65
+ continue;
66
+ }
67
+ const headingMatch = trimmed.match(/^#\s+(.+)$/);
68
+ if (headingMatch) {
69
+ blocks.push({
70
+ type: "header",
71
+ text: {
72
+ type: "plain_text",
73
+ text: headingMatch[1].trim().slice(0, 150),
74
+ },
75
+ });
76
+ continue;
77
+ }
78
+ blocks.push({
79
+ type: "section",
80
+ text: {
81
+ type: "mrkdwn",
82
+ text: ChatOutboundHandler.markdownToSlackMrkdwn(trimmed).slice(0, 3_000),
83
+ },
84
+ });
85
+ }
86
+ return blocks;
87
+ }
88
+ static markdownToDiscordEmbed(markdown) {
89
+ const normalized = markdown.trim();
90
+ const headingMatch = normalized.match(/^#\s+(.+?)(?:\n+([\s\S]*))?$/);
91
+ if (!headingMatch && normalized.length < 500) {
92
+ return null;
93
+ }
94
+ const title = headingMatch?.[1]?.trim();
95
+ const description = (headingMatch?.[2] ?? normalized).trim();
96
+ return {
97
+ title: title || undefined,
98
+ description: description || normalized,
99
+ };
100
+ }
101
+ resolveAdapter(platform) {
102
+ if (platform) {
103
+ const adapter = this.adaptersByPlatform.get(platform);
104
+ if (!adapter) {
105
+ throw new ValidationError(`Platform "${platform}" is not connected.`, {
106
+ recoveryHint: "Start the chat daemon with credentials for the requested platform.",
107
+ });
108
+ }
109
+ return adapter;
110
+ }
111
+ const adapters = [...this.adaptersByPlatform.values()];
112
+ if (adapters.length === 1) {
113
+ return adapters[0];
114
+ }
115
+ throw new ValidationError("platform is required when multiple chat platforms are connected.", {
116
+ recoveryHint: "Specify platform: slack or discord.",
117
+ });
118
+ }
119
+ async resolveChannelName(adapter, target) {
120
+ if (!target.channelId?.startsWith("#")) {
121
+ return target;
122
+ }
123
+ const name = target.channelId.slice(1);
124
+ const found = (await adapter.getChannels()).find((channel) => channel.name === name);
125
+ if (!found) {
126
+ throw new ValidationError(`Channel "${target.channelId}" not found.`, {
127
+ recoveryHint: "Use chat_list_channels to find available channel names and IDs.",
128
+ });
129
+ }
130
+ return {
131
+ ...target,
132
+ channelId: found.id,
133
+ };
134
+ }
135
+ buildMessage(platform, content) {
136
+ if (platform === PlatformType.Slack) {
137
+ try {
138
+ return {
139
+ content: ChatOutboundHandler.markdownToSlackMrkdwn(content),
140
+ format: "plain_text",
141
+ metadata: {
142
+ blocks: ChatOutboundHandler.markdownToSlackBlocks(content),
143
+ plainText: content,
144
+ },
145
+ };
146
+ }
147
+ catch {
148
+ return {
149
+ content: ChatOutboundHandler.markdownToSlackMrkdwn(content),
150
+ format: "plain_text",
151
+ };
152
+ }
153
+ }
154
+ try {
155
+ const embed = ChatOutboundHandler.markdownToDiscordEmbed(content);
156
+ if (embed) {
157
+ return {
158
+ content,
159
+ format: "markdown",
160
+ metadata: {
161
+ embed,
162
+ plainText: content,
163
+ },
164
+ };
165
+ }
166
+ }
167
+ catch {
168
+ // Fall back to plain markdown below.
169
+ }
170
+ return {
171
+ content,
172
+ format: "markdown",
173
+ };
174
+ }
175
+ }
176
+ function splitMarkdownSegments(markdown) {
177
+ const lines = markdown.replace(/\r\n/g, "\n").split("\n");
178
+ const segments = [];
179
+ const buffer = [];
180
+ let inCodeBlock = false;
181
+ const flush = () => {
182
+ if (buffer.length === 0) {
183
+ return;
184
+ }
185
+ segments.push(buffer.join("\n"));
186
+ buffer.length = 0;
187
+ };
188
+ for (const line of lines) {
189
+ const trimmed = line.trimStart();
190
+ if (trimmed.startsWith("```")) {
191
+ inCodeBlock = !inCodeBlock;
192
+ buffer.push(line);
193
+ if (!inCodeBlock) {
194
+ flush();
195
+ }
196
+ continue;
197
+ }
198
+ if (!inCodeBlock && line.trim() === "") {
199
+ flush();
200
+ continue;
201
+ }
202
+ buffer.push(line);
203
+ }
204
+ flush();
205
+ return segments;
206
+ }
207
+ //# sourceMappingURL=chat-outbound-handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chat-outbound-handler.js","sourceRoot":"","sources":["../../../src/application/chat/chat-outbound-handler.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ import type { PlatformType } from "../../domain/chat/platform-types.js";
2
+ import type { ChatPlatformAdapter, UserQuestionRepository } from "../../domain/chat/ports.js";
3
+ import { ThreadKey } from "../../domain/chat/session-bridge.js";
4
+ import { UserQuestion } from "../../domain/chat/user-question.js";
5
+ import type { InboundChatEvent } from "../../domain/chat/types.js";
6
+ export interface AskUserResult {
7
+ answered: boolean;
8
+ response?: string;
9
+ user_id?: string;
10
+ timestamp?: string;
11
+ reason?: "timeout";
12
+ }
13
+ export declare class InMemoryUserQuestionRepository implements UserQuestionRepository {
14
+ private readonly questions;
15
+ findPendingByThreadKey(key: ThreadKey): UserQuestion | null;
16
+ save(question: UserQuestion): void;
17
+ }
18
+ export declare class ChatQuestionService {
19
+ private readonly deps;
20
+ private adaptersByPlatform;
21
+ private readonly pendingQuestions;
22
+ private maxMessageLength;
23
+ constructor(deps: {
24
+ userQuestionRepository: UserQuestionRepository;
25
+ adapters?: ChatPlatformAdapter[];
26
+ now?: () => Date;
27
+ questionIdFactory?: () => string;
28
+ });
29
+ setAdapters(adapters: ChatPlatformAdapter[]): void;
30
+ configure(options: {
31
+ maxMessageLength?: number;
32
+ }): void;
33
+ findPendingByThreadKey(key: ThreadKey): UserQuestion | null;
34
+ ask(userId: string, questionText: string, timeoutSeconds?: number, platform?: PlatformType, channel?: string): Promise<AskUserResult>;
35
+ handlePotentialAnswer(event: InboundChatEvent): Promise<boolean>;
36
+ private resolveAdapter;
37
+ }
@@ -0,0 +1,135 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { ValidationError } from "../../core/types.js";
3
+ import { MessageCleaner } from "../../domain/chat/message-cleaner.js";
4
+ import { ThreadKey } from "../../domain/chat/session-bridge.js";
5
+ import { QuestionState, UserQuestion, } from "../../domain/chat/user-question.js";
6
+ export class InMemoryUserQuestionRepository {
7
+ questions = new Map();
8
+ findPendingByThreadKey(key) {
9
+ for (const question of this.questions.values()) {
10
+ if (question.state === QuestionState.Pending &&
11
+ question.replyScope?.equals(key)) {
12
+ return question;
13
+ }
14
+ }
15
+ return null;
16
+ }
17
+ save(question) {
18
+ this.questions.set(question.questionId, question);
19
+ }
20
+ }
21
+ export class ChatQuestionService {
22
+ deps;
23
+ adaptersByPlatform;
24
+ pendingQuestions = new Map();
25
+ maxMessageLength;
26
+ constructor(deps) {
27
+ this.deps = deps;
28
+ this.adaptersByPlatform = new Map();
29
+ this.maxMessageLength = 10_000;
30
+ this.setAdapters(deps.adapters ?? []);
31
+ }
32
+ setAdapters(adapters) {
33
+ this.adaptersByPlatform = new Map(adapters.map((adapter) => [adapter.platform, adapter]));
34
+ }
35
+ configure(options) {
36
+ if (options.maxMessageLength !== undefined) {
37
+ this.maxMessageLength = Math.max(1, Math.floor(options.maxMessageLength));
38
+ }
39
+ }
40
+ findPendingByThreadKey(key) {
41
+ return this.deps.userQuestionRepository.findPendingByThreadKey(key);
42
+ }
43
+ async ask(userId, questionText, timeoutSeconds = 300, platform, channel) {
44
+ const adapter = this.resolveAdapter(platform);
45
+ const normalizedTimeout = Math.max(1, Math.floor(timeoutSeconds));
46
+ const question = UserQuestion.ask(this.deps.questionIdFactory?.() ?? randomUUID(), userId, questionText, normalizedTimeout);
47
+ this.deps.userQuestionRepository.save(question);
48
+ const sendResult = await adapter.sendMessage({
49
+ platform: adapter.platform,
50
+ userId,
51
+ channelId: channel,
52
+ }, {
53
+ content: questionText,
54
+ format: "markdown",
55
+ });
56
+ const replyChannelId = sendResult.channelId ?? channel;
57
+ if (!replyChannelId) {
58
+ throw new ValidationError("Unable to determine reply channel for chat_ask_user.", {
59
+ recoveryHint: "Ensure the chat adapter returns channelId for sent messages or pass channel explicitly.",
60
+ });
61
+ }
62
+ question.markSent(new ThreadKey({
63
+ platform: adapter.platform,
64
+ teamOrGuildId: sendResult.teamOrGuildId,
65
+ channelId: replyChannelId,
66
+ threadId: sendResult.threadId,
67
+ }));
68
+ this.deps.userQuestionRepository.save(question);
69
+ return new Promise((resolve) => {
70
+ const timer = setTimeout(() => {
71
+ if (question.state === QuestionState.Pending) {
72
+ question.markTimedOut();
73
+ this.deps.userQuestionRepository.save(question);
74
+ }
75
+ this.pendingQuestions.delete(question.questionId);
76
+ resolve({
77
+ answered: false,
78
+ reason: "timeout",
79
+ });
80
+ }, normalizedTimeout * 1_000);
81
+ this.pendingQuestions.set(question.questionId, {
82
+ question,
83
+ resolve,
84
+ timer,
85
+ });
86
+ });
87
+ }
88
+ async handlePotentialAnswer(event) {
89
+ const threadKey = new ThreadKey({
90
+ platform: event.platform,
91
+ teamOrGuildId: event.teamOrGuildId,
92
+ channelId: event.channelId,
93
+ threadId: event.threadId,
94
+ });
95
+ const question = this.deps.userQuestionRepository.findPendingByThreadKey(threadKey);
96
+ if (!question) {
97
+ return false;
98
+ }
99
+ const pending = this.pendingQuestions.get(question.questionId);
100
+ if (!pending) {
101
+ return false;
102
+ }
103
+ const cleaned = MessageCleaner.clean(event.rawText, event.platform, undefined, this.maxMessageLength).cleanedText;
104
+ question.receiveAnswer(cleaned);
105
+ this.deps.userQuestionRepository.save(question);
106
+ clearTimeout(pending.timer);
107
+ this.pendingQuestions.delete(question.questionId);
108
+ pending.resolve({
109
+ answered: true,
110
+ response: cleaned,
111
+ user_id: event.userId,
112
+ timestamp: event.timestamp,
113
+ });
114
+ return true;
115
+ }
116
+ resolveAdapter(platform) {
117
+ if (platform) {
118
+ const adapter = this.adaptersByPlatform.get(platform);
119
+ if (!adapter) {
120
+ throw new ValidationError(`Platform "${platform}" is not connected.`, {
121
+ recoveryHint: "Specify a connected platform or start the chat daemon first.",
122
+ });
123
+ }
124
+ return adapter;
125
+ }
126
+ const adapters = [...this.adaptersByPlatform.values()];
127
+ if (adapters.length === 1) {
128
+ return adapters[0];
129
+ }
130
+ throw new ValidationError("platform is required when multiple chat platforms are connected.", {
131
+ recoveryHint: "Specify platform: slack or discord.",
132
+ });
133
+ }
134
+ }
135
+ //# sourceMappingURL=chat-question-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chat-question-service.js","sourceRoot":"","sources":["../../../src/application/chat/chat-question-service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAEtD,OAAO,EAAE,cAAc,EAAE,MAAM,sCAAsC,CAAC;AAEtE,OAAO,EAAE,SAAS,EAAE,MAAM,qCAAqC,CAAC;AAChE,OAAO,EACL,aAAa,EACb,YAAY,GACb,MAAM,oCAAoC,CAAC;AAW5C,MAAM,OAAO,8BAA8B;IACxB,SAAS,GAAG,IAAI,GAAG,EAAwB,CAAC;IAEtD,sBAAsB,CAAC,GAAc;QAC1C,KAAK,MAAM,QAAQ,IAAI,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,EAAE,CAAC;YAC/C,IACE,QAAQ,CAAC,KAAK,KAAK,aAAa,CAAC,OAAO;gBACxC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC,GAAG,CAAC,EAChC,CAAC;gBACD,OAAO,QAAQ,CAAC;YAClB,CAAC;QACH,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAEM,IAAI,CAAC,QAAsB;QAChC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;IACpD,CAAC;CACF;AAQD,MAAM,OAAO,mBAAmB;IAMX;IALX,kBAAkB,CAAyC;IAClD,gBAAgB,GAAG,IAAI,GAAG,EAA2B,CAAC;IAC/D,gBAAgB,CAAS;IAEjC,YACmB,IAKhB;QALgB,SAAI,GAAJ,IAAI,CAKpB;QAED,IAAI,CAAC,kBAAkB,GAAG,IAAI,GAAG,EAAE,CAAC;QACpC,IAAI,CAAC,gBAAgB,GAAG,MAAM,CAAC;QAC/B,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC;IACxC,CAAC;IAEM,WAAW,CAAC,QAA+B;QAChD,IAAI,CAAC,kBAAkB,GAAG,IAAI,GAAG,CAC/B,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,CACvD,CAAC;IACJ,CAAC;IAEM,SAAS,CAAC,OAAsC;QACrD,IAAI,OAAO,CAAC,gBAAgB,KAAK,SAAS,EAAE,CAAC;YAC3C,IAAI,CAAC,gBAAgB,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC,CAAC;QAC5E,CAAC;IACH,CAAC;IAEM,sBAAsB,CAAC,GAAc;QAC1C,OAAO,IAAI,CAAC,IAAI,CAAC,sBAAsB,CAAC,sBAAsB,CAAC,GAAG,CAAC,CAAC;IACtE,CAAC;IAEM,KAAK,CAAC,GAAG,CACd,MAAc,EACd,YAAoB,EACpB,cAAc,GAAG,GAAG,EACpB,QAAuB,EACvB,OAAgB;QAEhB,MAAM,OAAO,GAAG,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC;QAC9C,MAAM,iBAAiB,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC;QAClE,MAAM,QAAQ,GAAG,YAAY,CAAC,GAAG,CAC/B,IAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE,EAAE,IAAI,UAAU,EAAE,EAC/C,MAAM,EACN,YAAY,EACZ,iBAAiB,CAClB,CAAC;QACF,IAAI,CAAC,IAAI,CAAC,sBAAsB,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAEhD,MAAM,UAAU,GAAG,MAAM,OAAO,CAAC,WAAW,CAC1C;YACE,QAAQ,EAAE,OAAO,CAAC,QAAQ;YAC1B,MAAM;YACN,SAAS,EAAE,OAAO;SACnB,EACD;YACE,OAAO,EAAE,YAAY;YACrB,MAAM,EAAE,UAAU;SACnB,CACF,CAAC;QAEF,MAAM,cAAc,GAAG,UAAU,CAAC,SAAS,IAAI,OAAO,CAAC;QACvD,IAAI,CAAC,cAAc,EAAE,CAAC;YACpB,MAAM,IAAI,eAAe,CAAC,sDAAsD,EAAE;gBAChF,YAAY,EACV,yFAAyF;aAC5F,CAAC,CAAC;QACL,CAAC;QAED,QAAQ,CAAC,QAAQ,CACf,IAAI,SAAS,CAAC;YACZ,QAAQ,EAAE,OAAO,CAAC,QAAQ;YAC1B,aAAa,EAAE,UAAU,CAAC,aAAa;YACvC,SAAS,EAAE,cAAc;YACzB,QAAQ,EAAE,UAAU,CAAC,QAAQ;SAC9B,CAAC,CACH,CAAC;QACF,IAAI,CAAC,IAAI,CAAC,sBAAsB,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAEhD,OAAO,IAAI,OAAO,CAAgB,CAAC,OAAO,EAAE,EAAE;YAC5C,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;gBAC5B,IAAI,QAAQ,CAAC,KAAK,KAAK,aAAa,CAAC,OAAO,EAAE,CAAC;oBAC7C,QAAQ,CAAC,YAAY,EAAE,CAAC;oBACxB,IAAI,CAAC,IAAI,CAAC,sBAAsB,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;gBAClD,CAAC;gBACD,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;gBAClD,OAAO,CAAC;oBACN,QAAQ,EAAE,KAAK;oBACf,MAAM,EAAE,SAAS;iBAClB,CAAC,CAAC;YACL,CAAC,EAAE,iBAAiB,GAAG,KAAK,CAAC,CAAC;YAE9B,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,QAAQ,CAAC,UAAU,EAAE;gBAC7C,QAAQ;gBACR,OAAO;gBACP,KAAK;aACN,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC;IAEM,KAAK,CAAC,qBAAqB,CAAC,KAAuB;QACxD,MAAM,SAAS,GAAG,IAAI,SAAS,CAAC;YAC9B,QAAQ,EAAE,KAAK,CAAC,QAAQ;YACxB,aAAa,EAAE,KAAK,CAAC,aAAa;YAClC,SAAS,EAAE,KAAK,CAAC,SAAS;YAC1B,QAAQ,EAAE,KAAK,CAAC,QAAQ;SACzB,CAAC,CAAC;QACH,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,sBAAsB,CAAC,sBAAsB,CAAC,SAAS,CAAC,CAAC;QACpF,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,OAAO,KAAK,CAAC;QACf,CAAC;QAED,MAAM,OAAO,GAAG,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;QAC/D,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO,KAAK,CAAC;QACf,CAAC;QAED,MAAM,OAAO,GAAG,cAAc,CAAC,KAAK,CAClC,KAAK,CAAC,OAAO,EACb,KAAK,CAAC,QAAQ,EACd,SAAS,EACT,IAAI,CAAC,gBAAgB,CACtB,CAAC,WAAW,CAAC;QACd,QAAQ,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;QAChC,IAAI,CAAC,IAAI,CAAC,sBAAsB,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAEhD,YAAY,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QAC5B,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;QAClD,OAAO,CAAC,OAAO,CAAC;YACd,QAAQ,EAAE,IAAI;YACd,QAAQ,EAAE,OAAO;YACjB,OAAO,EAAE,KAAK,CAAC,MAAM;YACrB,SAAS,EAAE,KAAK,CAAC,SAAS;SAC3B,CAAC,CAAC;QACH,OAAO,IAAI,CAAC;IACd,CAAC;IAEO,cAAc,CAAC,QAAuB;QAC5C,IAAI,QAAQ,EAAE,CAAC;YACb,MAAM,OAAO,GAAG,IAAI,CAAC,kBAAkB,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;YACtD,IAAI,CAAC,OAAO,EAAE,CAAC;gBACb,MAAM,IAAI,eAAe,CAAC,aAAa,QAAQ,qBAAqB,EAAE;oBACpE,YAAY,EAAE,8DAA8D;iBAC7E,CAAC,CAAC;YACL,CAAC;YACD,OAAO,OAAO,CAAC;QACjB,CAAC;QAED,MAAM,QAAQ,GAAG,CAAC,GAAG,IAAI,CAAC,kBAAkB,CAAC,MAAM,EAAE,CAAC,CAAC;QACvD,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC1B,OAAO,QAAQ,CAAC,CAAC,CAAE,CAAC;QACtB,CAAC;QAED,MAAM,IAAI,eAAe,CACvB,kEAAkE,EAClE;YACE,YAAY,EAAE,qCAAqC;SACpD,CACF,CAAC;IACJ,CAAC;CACF"}