@rk0429/agentic-relay 21.2.3 → 22.0.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 (106) hide show
  1. package/dist/application/chat/chat-daemon-service.d.ts +81 -0
  2. package/dist/application/chat/chat-daemon-service.js +382 -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 +175 -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 +16 -0
  11. package/dist/application/chat/chat-outbound-handler.js +190 -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/routine-status-query-service.d.ts +17 -0
  23. package/dist/application/routine-status-query-service.js +111 -0
  24. package/dist/application/routine-status-query-service.js.map +1 -1
  25. package/dist/bin/relay.d.ts +9 -2
  26. package/dist/bin/relay.js +296 -18
  27. package/dist/bin/relay.js.map +1 -1
  28. package/dist/core/chat-types.d.ts +18 -0
  29. package/dist/core/chat-types.js +14 -0
  30. package/dist/core/chat-types.js.map +1 -0
  31. package/dist/core/types.d.ts +7 -0
  32. package/dist/domain/chat/message-cleaner.d.ts +11 -0
  33. package/dist/domain/chat/message-cleaner.js +45 -0
  34. package/dist/domain/chat/message-cleaner.js.map +1 -0
  35. package/dist/domain/chat/message-router.d.ts +8 -0
  36. package/dist/domain/chat/message-router.js +93 -0
  37. package/dist/domain/chat/message-router.js.map +1 -0
  38. package/dist/domain/chat/message-splitter.d.ts +3 -0
  39. package/dist/domain/chat/message-splitter.js +148 -0
  40. package/dist/domain/chat/message-splitter.js.map +1 -0
  41. package/dist/domain/chat/platform-connection.d.ts +31 -0
  42. package/dist/domain/chat/platform-connection.js +67 -0
  43. package/dist/domain/chat/platform-connection.js.map +1 -0
  44. package/dist/domain/chat/ports.d.ts +31 -0
  45. package/dist/domain/chat/ports.js +2 -0
  46. package/dist/domain/chat/ports.js.map +1 -0
  47. package/dist/domain/chat/session-bridge.d.ts +41 -0
  48. package/dist/domain/chat/session-bridge.js +93 -0
  49. package/dist/domain/chat/session-bridge.js.map +1 -0
  50. package/dist/domain/chat/types.d.ts +83 -0
  51. package/dist/domain/chat/types.js +2 -0
  52. package/dist/domain/chat/types.js.map +1 -0
  53. package/dist/domain/chat/user-question.d.ts +33 -0
  54. package/dist/domain/chat/user-question.js +73 -0
  55. package/dist/domain/chat/user-question.js.map +1 -0
  56. package/dist/domain/config.d.ts +3 -1
  57. package/dist/domain/config.js +9 -0
  58. package/dist/domain/config.js.map +1 -1
  59. package/dist/domain/routine-loader.d.ts +2 -0
  60. package/dist/domain/routine-loader.js +3 -0
  61. package/dist/domain/routine-loader.js.map +1 -1
  62. package/dist/infrastructure/chat/adapter-factory.d.ts +5 -0
  63. package/dist/infrastructure/chat/adapter-factory.js +15 -0
  64. package/dist/infrastructure/chat/adapter-factory.js.map +1 -0
  65. package/dist/infrastructure/chat/adapters/discord-adapter.d.ts +23 -0
  66. package/dist/infrastructure/chat/adapters/discord-adapter.js +199 -0
  67. package/dist/infrastructure/chat/adapters/discord-adapter.js.map +1 -0
  68. package/dist/infrastructure/chat/adapters/slack-adapter.d.ts +24 -0
  69. package/dist/infrastructure/chat/adapters/slack-adapter.js +155 -0
  70. package/dist/infrastructure/chat/adapters/slack-adapter.js.map +1 -0
  71. package/dist/infrastructure/chat/agent-gateway-impl.d.ts +21 -0
  72. package/dist/infrastructure/chat/agent-gateway-impl.js +101 -0
  73. package/dist/infrastructure/chat/agent-gateway-impl.js.map +1 -0
  74. package/dist/infrastructure/chat/chat-ipc-client.d.ts +33 -0
  75. package/dist/infrastructure/chat/chat-ipc-client.js +70 -0
  76. package/dist/infrastructure/chat/chat-ipc-client.js.map +1 -0
  77. package/dist/infrastructure/chat/chat-ipc-server.d.ts +19 -0
  78. package/dist/infrastructure/chat/chat-ipc-server.js +125 -0
  79. package/dist/infrastructure/chat/chat-ipc-server.js.map +1 -0
  80. package/dist/infrastructure/chat/chat-logger.d.ts +24 -0
  81. package/dist/infrastructure/chat/chat-logger.js +86 -0
  82. package/dist/infrastructure/chat/chat-logger.js.map +1 -0
  83. package/dist/infrastructure/chat/credentials-repository.d.ts +8 -0
  84. package/dist/infrastructure/chat/credentials-repository.js +84 -0
  85. package/dist/infrastructure/chat/credentials-repository.js.map +1 -0
  86. package/dist/infrastructure/chat/session-bridge-repository.d.ts +13 -0
  87. package/dist/infrastructure/chat/session-bridge-repository.js +120 -0
  88. package/dist/infrastructure/chat/session-bridge-repository.js.map +1 -0
  89. package/dist/infrastructure/chat/token-masking.d.ts +3 -0
  90. package/dist/infrastructure/chat/token-masking.js +22 -0
  91. package/dist/infrastructure/chat/token-masking.js.map +1 -0
  92. package/dist/infrastructure/store/relay-store.js +3 -1
  93. package/dist/infrastructure/store/relay-store.js.map +1 -1
  94. package/dist/interfaces/cli/chat-cli.d.ts +34 -0
  95. package/dist/interfaces/cli/chat-cli.js +68 -0
  96. package/dist/interfaces/cli/chat-cli.js.map +1 -0
  97. package/dist/interfaces/cli/relay-cli-args.d.ts +16 -1
  98. package/dist/interfaces/cli/relay-cli-args.js +135 -2
  99. package/dist/interfaces/cli/relay-cli-args.js.map +1 -1
  100. package/dist/interfaces/mcp/chat-tools.d.ts +33 -0
  101. package/dist/interfaces/mcp/chat-tools.js +129 -0
  102. package/dist/interfaces/mcp/chat-tools.js.map +1 -0
  103. package/dist/interfaces/mcp/relay-mcp-server.d.ts +2 -0
  104. package/dist/interfaces/mcp/relay-mcp-server.js +194 -1
  105. package/dist/interfaces/mcp/relay-mcp-server.js.map +1 -1
  106. package/package.json +3 -1
@@ -0,0 +1,175 @@
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
+ return;
80
+ }
81
+ if (intent.kind === "new_session") {
82
+ await this.handleNewSession(event, intent, durationMs);
83
+ return;
84
+ }
85
+ await this.handleContinuation(event, intent, durationMs);
86
+ }
87
+ finally {
88
+ await stopIndicator?.();
89
+ this.deps.messageQueueManager.finishProcessing(queueKey);
90
+ this.activeOperations = Math.max(0, this.activeOperations - 1);
91
+ if (this.activeOperations === 0) {
92
+ for (const resolve of this.idleWaiters) {
93
+ resolve();
94
+ }
95
+ this.idleWaiters.clear();
96
+ }
97
+ for (const buffered of this.deps.messageQueueManager.dequeueAll(queueKey)) {
98
+ await this.handleInbound(buffered);
99
+ }
100
+ }
101
+ }
102
+ async waitForIdle(timeoutMs) {
103
+ if (this.activeOperations === 0) {
104
+ return;
105
+ }
106
+ await Promise.race([
107
+ new Promise((resolve) => this.idleWaiters.add(resolve)),
108
+ new Promise((resolve) => setTimeout(resolve, timeoutMs)),
109
+ ]);
110
+ }
111
+ async handleNewSession(event, intent, durationMs) {
112
+ const result = await this.deps.agentGateway.startSession(intent.cleanedText, intent.backendSpec);
113
+ const sendResult = await this.deps.outboundHandler.sendResponse(event.platform, {
114
+ channelId: event.channelId,
115
+ threadId: event.threadId,
116
+ userId: event.isDM ? event.userId : undefined,
117
+ messageId: event.messageId,
118
+ }, result.response);
119
+ const bridge = SessionBridge.create(new ThreadKey({
120
+ platform: event.platform,
121
+ teamOrGuildId: sendResult.teamOrGuildId ?? event.teamOrGuildId,
122
+ channelId: sendResult.channelId ?? event.channelId,
123
+ threadId: sendResult.threadId ?? event.threadId,
124
+ }), result.relaySessionId, result.backend);
125
+ bridge.backendSessionId = result.backendSessionId ?? bridge.backendSessionId;
126
+ this.deps.sessionBridgeRepository.save(bridge);
127
+ await this.log("info", "chat session started", {
128
+ platform: event.platform,
129
+ relaySessionId: result.relaySessionId,
130
+ backend: result.backend,
131
+ threadKey: bridge.threadKey.toString(),
132
+ intent: intent.kind,
133
+ durationMs: durationMs(),
134
+ });
135
+ }
136
+ async handleContinuation(event, intent, durationMs) {
137
+ intent.bridge.startProcessing();
138
+ this.deps.sessionBridgeRepository.save(intent.bridge);
139
+ const result = await this.deps.agentGateway.continueSession(intent.bridge.relaySessionId, intent.cleanedText);
140
+ const sendResult = await this.deps.outboundHandler.sendResponse(event.platform, {
141
+ channelId: intent.bridge.threadKey.channelId,
142
+ threadId: intent.bridge.threadKey.threadId,
143
+ userId: event.isDM ? event.userId : undefined,
144
+ messageId: event.messageId,
145
+ }, result.response);
146
+ const updatedBridge = new SessionBridge({
147
+ threadKey: new ThreadKey({
148
+ platform: event.platform,
149
+ teamOrGuildId: sendResult.teamOrGuildId ?? intent.bridge.threadKey.teamOrGuildId,
150
+ channelId: sendResult.channelId ?? intent.bridge.threadKey.channelId,
151
+ threadId: sendResult.threadId ?? intent.bridge.threadKey.threadId,
152
+ }),
153
+ relaySessionId: result.relaySessionId,
154
+ backendSessionId: result.backendSessionId ?? intent.bridge.backendSessionId,
155
+ backend: result.backend,
156
+ createdAt: intent.bridge.createdAt,
157
+ lastActivityAt: new Date(),
158
+ isProcessing: false,
159
+ });
160
+ updatedBridge.finishProcessing();
161
+ this.deps.sessionBridgeRepository.save(updatedBridge);
162
+ await this.log("info", "chat session continued", {
163
+ platform: event.platform,
164
+ relaySessionId: result.relaySessionId,
165
+ backend: result.backend,
166
+ threadKey: updatedBridge.threadKey.toString(),
167
+ intent: intent.kind,
168
+ durationMs: durationMs(),
169
+ });
170
+ }
171
+ async log(level, message, metadata) {
172
+ await emitChatLog(this.logger, level, message, metadata);
173
+ }
174
+ }
175
+ //# 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,16 @@
1
+ import { PlatformType, type ChatChannel } from "../../core/chat-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 buildMessage;
16
+ }
@@ -0,0 +1,190 @@
1
+ import { ValidationError } from "../../core/errors.js";
2
+ import { PlatformType } from "../../core/chat-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 maxLength = adapter.platform === PlatformType.Slack ? 4_000 : 2_000;
16
+ const pieces = MessageSplitter.split(content, maxLength);
17
+ const baseTarget = {
18
+ platform: adapter.platform,
19
+ ...target,
20
+ };
21
+ let replyTarget = baseTarget;
22
+ let firstResult;
23
+ for (const piece of pieces) {
24
+ const message = this.buildMessage(adapter.platform, piece);
25
+ const result = await adapter.sendMessage(replyTarget, message);
26
+ firstResult ??= result;
27
+ if (!replyTarget.threadId && result.threadId) {
28
+ replyTarget = {
29
+ ...replyTarget,
30
+ channelId: result.channelId ?? replyTarget.channelId,
31
+ threadId: result.threadId,
32
+ messageId: undefined,
33
+ };
34
+ }
35
+ }
36
+ return firstResult ?? {
37
+ success: true,
38
+ messageId: "",
39
+ channelId: target.channelId,
40
+ threadId: target.threadId,
41
+ };
42
+ }
43
+ async listChannels(platform) {
44
+ const adapter = this.resolveAdapter(platform);
45
+ return adapter.getChannels();
46
+ }
47
+ async beginProcessingIndicator(event) {
48
+ const adapter = this.adaptersByPlatform.get(event.platform);
49
+ const stopIndicator = await adapter?.beginProcessingIndicator?.(event);
50
+ return typeof stopIndicator === "function" ? stopIndicator : undefined;
51
+ }
52
+ static markdownToSlackMrkdwn(markdown) {
53
+ return markdown
54
+ .replace(/\r\n/g, "\n")
55
+ .replace(/^#\s+(.+)$/gm, (_, title) => `*${title.trim()}*`)
56
+ .replace(/\*\*(.+?)\*\*/g, "*$1*");
57
+ }
58
+ static markdownToSlackBlocks(markdown) {
59
+ const blocks = [];
60
+ const segments = splitMarkdownSegments(markdown);
61
+ for (const segment of segments) {
62
+ const trimmed = segment.trim();
63
+ if (!trimmed) {
64
+ continue;
65
+ }
66
+ const headingMatch = trimmed.match(/^#\s+(.+)$/);
67
+ if (headingMatch) {
68
+ blocks.push({
69
+ type: "header",
70
+ text: {
71
+ type: "plain_text",
72
+ text: headingMatch[1].trim().slice(0, 150),
73
+ },
74
+ });
75
+ continue;
76
+ }
77
+ blocks.push({
78
+ type: "section",
79
+ text: {
80
+ type: "mrkdwn",
81
+ text: ChatOutboundHandler.markdownToSlackMrkdwn(trimmed).slice(0, 3_000),
82
+ },
83
+ });
84
+ }
85
+ return blocks;
86
+ }
87
+ static markdownToDiscordEmbed(markdown) {
88
+ const normalized = markdown.trim();
89
+ const headingMatch = normalized.match(/^#\s+(.+?)(?:\n+([\s\S]*))?$/);
90
+ if (!headingMatch && normalized.length < 500) {
91
+ return null;
92
+ }
93
+ const title = headingMatch?.[1]?.trim();
94
+ const description = (headingMatch?.[2] ?? normalized).trim();
95
+ return {
96
+ title: title || undefined,
97
+ description: description || normalized,
98
+ };
99
+ }
100
+ resolveAdapter(platform) {
101
+ if (platform) {
102
+ const adapter = this.adaptersByPlatform.get(platform);
103
+ if (!adapter) {
104
+ throw new ValidationError(`Platform "${platform}" is not connected.`, {
105
+ recoveryHint: "Start the chat daemon with credentials for the requested platform.",
106
+ });
107
+ }
108
+ return adapter;
109
+ }
110
+ const adapters = [...this.adaptersByPlatform.values()];
111
+ if (adapters.length === 1) {
112
+ return adapters[0];
113
+ }
114
+ throw new ValidationError("platform is required when multiple chat platforms are connected.", {
115
+ recoveryHint: "Specify platform: slack or discord.",
116
+ });
117
+ }
118
+ buildMessage(platform, content) {
119
+ if (platform === PlatformType.Slack) {
120
+ try {
121
+ return {
122
+ content: ChatOutboundHandler.markdownToSlackMrkdwn(content),
123
+ format: "plain_text",
124
+ metadata: {
125
+ blocks: ChatOutboundHandler.markdownToSlackBlocks(content),
126
+ plainText: content,
127
+ },
128
+ };
129
+ }
130
+ catch {
131
+ return {
132
+ content: ChatOutboundHandler.markdownToSlackMrkdwn(content),
133
+ format: "plain_text",
134
+ };
135
+ }
136
+ }
137
+ try {
138
+ const embed = ChatOutboundHandler.markdownToDiscordEmbed(content);
139
+ if (embed) {
140
+ return {
141
+ content,
142
+ format: "markdown",
143
+ metadata: {
144
+ embed,
145
+ plainText: content,
146
+ },
147
+ };
148
+ }
149
+ }
150
+ catch {
151
+ // Fall back to plain markdown below.
152
+ }
153
+ return {
154
+ content,
155
+ format: "markdown",
156
+ };
157
+ }
158
+ }
159
+ function splitMarkdownSegments(markdown) {
160
+ const lines = markdown.replace(/\r\n/g, "\n").split("\n");
161
+ const segments = [];
162
+ const buffer = [];
163
+ let inCodeBlock = false;
164
+ const flush = () => {
165
+ if (buffer.length === 0) {
166
+ return;
167
+ }
168
+ segments.push(buffer.join("\n"));
169
+ buffer.length = 0;
170
+ };
171
+ for (const line of lines) {
172
+ const trimmed = line.trimStart();
173
+ if (trimmed.startsWith("```")) {
174
+ inCodeBlock = !inCodeBlock;
175
+ buffer.push(line);
176
+ if (!inCodeBlock) {
177
+ flush();
178
+ }
179
+ continue;
180
+ }
181
+ if (!inCodeBlock && line.trim() === "") {
182
+ flush();
183
+ continue;
184
+ }
185
+ buffer.push(line);
186
+ }
187
+ flush();
188
+ return segments;
189
+ }
190
+ //# 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 "../../core/chat-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/errors.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":"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"}
@@ -0,0 +1,27 @@
1
+ import type { CredentialsRepository } from "../../domain/chat/ports.js";
2
+ export declare class ChatSetupService {
3
+ private readonly deps;
4
+ constructor(deps: {
5
+ credentialsRepository: CredentialsRepository;
6
+ prompt: (message: string) => Promise<string>;
7
+ confirm: (message: string) => Promise<boolean>;
8
+ openUrl: (url: string) => Promise<void>;
9
+ fetchImpl?: typeof fetch;
10
+ logger?: (message: string) => void;
11
+ });
12
+ setupSlack(): Promise<{
13
+ platform: "slack";
14
+ manifestUrl: string;
15
+ }>;
16
+ setupDiscord(): Promise<{
17
+ platform: "discord";
18
+ inviteUrl: string;
19
+ }>;
20
+ private validateSlackCredentials;
21
+ private validateDiscordCredentials;
22
+ private confirmOverwrite;
23
+ private openWithFallback;
24
+ private fetchJson;
25
+ }
26
+ export declare function buildSlackManifest(): Record<string, unknown>;
27
+ export declare function buildDiscordInviteUrl(applicationId: string): string;