pilotlynx 0.1.2 → 0.2.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 (145) hide show
  1. package/README.md +40 -40
  2. package/dist/agents/improve.agent.d.ts +42 -1
  3. package/dist/agents/improve.agent.js +102 -7
  4. package/dist/agents/improve.agent.js.map +1 -1
  5. package/dist/agents/run.agent.d.ts +0 -1
  6. package/dist/agents/run.agent.js +10 -7
  7. package/dist/agents/run.agent.js.map +1 -1
  8. package/dist/cli.js +8 -3
  9. package/dist/cli.js.map +1 -1
  10. package/dist/commands/audit.d.ts +2 -0
  11. package/dist/commands/audit.js +51 -0
  12. package/dist/commands/audit.js.map +1 -0
  13. package/dist/commands/eval.d.ts +2 -0
  14. package/dist/commands/eval.js +47 -0
  15. package/dist/commands/eval.js.map +1 -0
  16. package/dist/commands/improve.js +83 -5
  17. package/dist/commands/improve.js.map +1 -1
  18. package/dist/commands/init.js +17 -27
  19. package/dist/commands/init.js.map +1 -1
  20. package/dist/commands/insights.js +63 -0
  21. package/dist/commands/insights.js.map +1 -1
  22. package/dist/commands/logs.d.ts +1 -0
  23. package/dist/commands/logs.js +23 -1
  24. package/dist/commands/logs.js.map +1 -1
  25. package/dist/commands/relay.js +250 -124
  26. package/dist/commands/relay.js.map +1 -1
  27. package/dist/commands/run.js +21 -2
  28. package/dist/commands/run.js.map +1 -1
  29. package/dist/commands/schedule.js +7 -1
  30. package/dist/commands/schedule.js.map +1 -1
  31. package/dist/commands/status.js +9 -2
  32. package/dist/commands/status.js.map +1 -1
  33. package/dist/lib/agent-runner.d.ts +5 -1
  34. package/dist/lib/agent-runner.js +41 -1
  35. package/dist/lib/agent-runner.js.map +1 -1
  36. package/dist/lib/audit.d.ts +7 -0
  37. package/dist/lib/audit.js +63 -0
  38. package/dist/lib/audit.js.map +1 -0
  39. package/dist/lib/callbacks.d.ts +12 -1
  40. package/dist/lib/callbacks.js +147 -19
  41. package/dist/lib/callbacks.js.map +1 -1
  42. package/dist/lib/command-ops/doctor-ops.js +41 -6
  43. package/dist/lib/command-ops/doctor-ops.js.map +1 -1
  44. package/dist/lib/command-ops/eval-ops.d.ts +7 -0
  45. package/dist/lib/command-ops/eval-ops.js +111 -0
  46. package/dist/lib/command-ops/eval-ops.js.map +1 -0
  47. package/dist/lib/command-ops/improve-ops.d.ts +14 -1
  48. package/dist/lib/command-ops/improve-ops.js +369 -17
  49. package/dist/lib/command-ops/improve-ops.js.map +1 -1
  50. package/dist/lib/command-ops/run-ops.d.ts +8 -1
  51. package/dist/lib/command-ops/run-ops.js +25 -4
  52. package/dist/lib/command-ops/run-ops.js.map +1 -1
  53. package/dist/lib/command-ops/secrets-migration-ops.js +1 -1
  54. package/dist/lib/command-ops/secrets-migration-ops.js.map +1 -1
  55. package/dist/lib/command-ops/status-ops.d.ts +1 -0
  56. package/dist/lib/command-ops/status-ops.js +19 -7
  57. package/dist/lib/command-ops/status-ops.js.map +1 -1
  58. package/dist/lib/config.js +3 -3
  59. package/dist/lib/config.js.map +1 -1
  60. package/dist/lib/cron.d.ts +1 -1
  61. package/dist/lib/cron.js +2 -2
  62. package/dist/lib/cron.js.map +1 -1
  63. package/dist/lib/global-config.js +1 -1
  64. package/dist/lib/global-config.js.map +1 -1
  65. package/dist/lib/observation.d.ts +60 -2
  66. package/dist/lib/observation.js +261 -13
  67. package/dist/lib/observation.js.map +1 -1
  68. package/dist/lib/registry.d.ts +0 -1
  69. package/dist/lib/registry.js +1 -1
  70. package/dist/lib/registry.js.map +1 -1
  71. package/dist/lib/relay/admin.d.ts +29 -0
  72. package/dist/lib/relay/admin.js +176 -0
  73. package/dist/lib/relay/admin.js.map +1 -0
  74. package/dist/lib/relay/bindings.d.ts +8 -0
  75. package/dist/lib/relay/bindings.js +50 -0
  76. package/dist/lib/relay/bindings.js.map +1 -0
  77. package/dist/lib/relay/config.d.ts +3 -3
  78. package/dist/lib/relay/config.js +18 -10
  79. package/dist/lib/relay/config.js.map +1 -1
  80. package/dist/lib/relay/context.d.ts +31 -0
  81. package/dist/lib/relay/context.js +118 -0
  82. package/dist/lib/relay/context.js.map +1 -0
  83. package/dist/lib/relay/db.d.ts +38 -0
  84. package/dist/lib/relay/db.js +252 -0
  85. package/dist/lib/relay/db.js.map +1 -0
  86. package/dist/lib/relay/executor.d.ts +2 -0
  87. package/dist/lib/relay/executor.js +108 -0
  88. package/dist/lib/relay/executor.js.map +1 -0
  89. package/dist/lib/relay/feedback.d.ts +29 -0
  90. package/dist/lib/relay/feedback.js +142 -0
  91. package/dist/lib/relay/feedback.js.map +1 -0
  92. package/dist/lib/relay/notifier.d.ts +30 -0
  93. package/dist/lib/relay/notifier.js +78 -0
  94. package/dist/lib/relay/notifier.js.map +1 -0
  95. package/dist/lib/relay/notify.d.ts +3 -4
  96. package/dist/lib/relay/notify.js +43 -159
  97. package/dist/lib/relay/notify.js.map +1 -1
  98. package/dist/lib/relay/platform.d.ts +52 -0
  99. package/dist/lib/relay/platform.js +5 -0
  100. package/dist/lib/relay/platform.js.map +1 -0
  101. package/dist/lib/relay/platforms/slack.d.ts +37 -0
  102. package/dist/lib/relay/platforms/slack.js +240 -0
  103. package/dist/lib/relay/platforms/slack.js.map +1 -0
  104. package/dist/lib/relay/platforms/telegram.d.ts +29 -0
  105. package/dist/lib/relay/platforms/telegram.js +193 -0
  106. package/dist/lib/relay/platforms/telegram.js.map +1 -0
  107. package/dist/lib/relay/poster.d.ts +24 -0
  108. package/dist/lib/relay/poster.js +136 -0
  109. package/dist/lib/relay/poster.js.map +1 -0
  110. package/dist/lib/relay/queue.d.ts +18 -0
  111. package/dist/lib/relay/queue.js +85 -0
  112. package/dist/lib/relay/queue.js.map +1 -0
  113. package/dist/lib/relay/router.d.ts +25 -2
  114. package/dist/lib/relay/router.js +259 -168
  115. package/dist/lib/relay/router.js.map +1 -1
  116. package/dist/lib/relay/service.d.ts +31 -7
  117. package/dist/lib/relay/service.js +281 -200
  118. package/dist/lib/relay/service.js.map +1 -1
  119. package/dist/lib/relay/types.d.ts +189 -34
  120. package/dist/lib/relay/types.js +68 -28
  121. package/dist/lib/relay/types.js.map +1 -1
  122. package/dist/lib/sandbox.d.ts +9 -1
  123. package/dist/lib/sandbox.js +17 -2
  124. package/dist/lib/sandbox.js.map +1 -1
  125. package/dist/lib/schedule.d.ts +4 -5
  126. package/dist/lib/schedule.js +7 -8
  127. package/dist/lib/schedule.js.map +1 -1
  128. package/dist/lib/secrets.js +11 -1
  129. package/dist/lib/secrets.js.map +1 -1
  130. package/dist/lib/types.d.ts +80 -0
  131. package/dist/lib/types.js +9 -1
  132. package/dist/lib/types.js.map +1 -1
  133. package/package.json +18 -18
  134. package/prompts/improve.yaml +114 -6
  135. package/prompts/relay.yaml +29 -0
  136. package/prompts/run.yaml +36 -2
  137. package/template/CLAUDE.md +34 -5
  138. package/template/RUNBOOK.md +5 -5
  139. package/template/evals/sample.json +16 -0
  140. package/template/memory/MEMORY.md +6 -0
  141. package/template/memory/procedures/.gitkeep +0 -0
  142. package/template/schedule.yaml +1 -1
  143. package/template/workflows/daily_feedback.ts +78 -2
  144. package/template/workflows/project_review.ts +51 -2
  145. package/prompts/relay-chat.yaml +0 -24
@@ -0,0 +1,240 @@
1
+ // ── Slack Platform Adapter ──
2
+ // Implements ChatPlatform via @slack/bolt (Socket Mode or HTTP).
3
+ export class SlackAdapter {
4
+ config;
5
+ name = 'slack';
6
+ capabilities = {
7
+ nativeStreaming: true,
8
+ maxStreamUpdateHz: 10,
9
+ supportsReactions: true,
10
+ supportsSlashCommands: true,
11
+ supportsThreads: true,
12
+ maxMessageLength: 4000,
13
+ };
14
+ onMessage = async () => { };
15
+ onReaction = async () => { };
16
+ onCommand = async () => '';
17
+ app; // Bolt App – dynamically imported
18
+ botUserId;
19
+ userCache = new Map(); // userId → displayName
20
+ reconnectBackoff = 1000;
21
+ lastEventTime = Date.now();
22
+ healthTimer;
23
+ processedRetries = new Set(); // dedup x-slack-retry-num
24
+ constructor(config) {
25
+ this.config = config;
26
+ }
27
+ async start() {
28
+ const { App } = await import('@slack/bolt');
29
+ this.app = new App({
30
+ token: this.config.botToken,
31
+ appToken: this.config.appToken,
32
+ signingSecret: this.config.signingSecret,
33
+ socketMode: this.config.mode === 'socket',
34
+ port: this.config.port,
35
+ });
36
+ // ── Event: app_mention ──
37
+ this.app.event('app_mention', async ({ event, context }) => {
38
+ if (this.shouldSkipRetry(context))
39
+ return;
40
+ this.lastEventTime = Date.now();
41
+ const msg = await this.slackEventToChatMessage(event);
42
+ if (!msg.isBot)
43
+ await this.onMessage(msg);
44
+ });
45
+ // ── Event: message ──
46
+ this.app.event('message', async ({ event, context }) => {
47
+ if (this.shouldSkipRetry(context))
48
+ return;
49
+ if (event.subtype && event.subtype !== 'file_share')
50
+ return;
51
+ this.lastEventTime = Date.now();
52
+ const msg = await this.slackEventToChatMessage(event);
53
+ if (!msg.isBot)
54
+ await this.onMessage(msg);
55
+ });
56
+ // ── Event: reaction_added ──
57
+ this.app.event('reaction_added', async ({ event, context }) => {
58
+ if (this.shouldSkipRetry(context))
59
+ return;
60
+ this.lastEventTime = Date.now();
61
+ await this.onReaction(event.item.channel, event.item.ts, event.user, event.reaction);
62
+ });
63
+ // ── Slash command: /pilotlynx-bind ──
64
+ this.app.command('/pilotlynx-bind', async ({ command, ack }) => {
65
+ await ack();
66
+ this.lastEventTime = Date.now();
67
+ const response = await this.onCommand(command.channel_id, command.user_id, 'bind', command.text);
68
+ return { text: response };
69
+ });
70
+ await this.app.start();
71
+ // Resolve bot user ID for self-filtering
72
+ const authResult = await this.app.client.auth.test();
73
+ this.botUserId = authResult.user_id;
74
+ // Health check timer – force reconnect if no events for 90s
75
+ this.healthTimer = setInterval(() => {
76
+ if (Date.now() - this.lastEventTime > 90_000) {
77
+ this.attemptReconnect();
78
+ }
79
+ }, 30_000);
80
+ }
81
+ async stop() {
82
+ if (this.healthTimer) {
83
+ clearInterval(this.healthTimer);
84
+ this.healthTimer = undefined;
85
+ }
86
+ if (this.app) {
87
+ await this.app.stop();
88
+ }
89
+ }
90
+ async sendMessage(channelId, text, threadId) {
91
+ const result = await this.app.client.chat.postMessage({
92
+ channel: channelId,
93
+ text,
94
+ thread_ts: threadId,
95
+ });
96
+ return result.ts;
97
+ }
98
+ async updateMessage(channelId, messageId, text) {
99
+ await this.app.client.chat.update({
100
+ channel: channelId,
101
+ ts: messageId,
102
+ text,
103
+ });
104
+ }
105
+ async startStream(channelId, threadId) {
106
+ const initialTs = await this.sendMessage(channelId, 'Working on it\u2026', threadId);
107
+ let accumulated = '';
108
+ let pending;
109
+ const debounceMs = 300;
110
+ const flush = async (text) => {
111
+ await this.updateMessage(channelId, initialTs, text);
112
+ };
113
+ return {
114
+ append: async (text) => {
115
+ accumulated += text;
116
+ if (pending)
117
+ clearTimeout(pending);
118
+ pending = setTimeout(() => {
119
+ void flush(accumulated);
120
+ }, debounceMs);
121
+ },
122
+ stop: async (finalText) => {
123
+ if (pending)
124
+ clearTimeout(pending);
125
+ await flush(finalText ?? accumulated);
126
+ },
127
+ };
128
+ }
129
+ async uploadFile(channelId, content, filename, threadId) {
130
+ await this.app.client.files.uploadV2({
131
+ channel_id: channelId,
132
+ content,
133
+ filename,
134
+ thread_ts: threadId,
135
+ });
136
+ }
137
+ async getThreadHistory(channelId, threadId, afterTs) {
138
+ const result = await this.app.client.conversations.replies({
139
+ channel: channelId,
140
+ ts: threadId,
141
+ oldest: afterTs,
142
+ });
143
+ const messages = [];
144
+ for (const msg of result.messages ?? []) {
145
+ messages.push(await this.slackMsgToChatMessage(channelId, msg));
146
+ }
147
+ return messages;
148
+ }
149
+ // ── Helpers ──
150
+ shouldSkipRetry(context) {
151
+ const retryNum = context?.retryNum;
152
+ if (retryNum == null)
153
+ return false;
154
+ const key = `${retryNum}`;
155
+ if (this.processedRetries.has(key))
156
+ return true;
157
+ this.processedRetries.add(key);
158
+ // Prevent memory leak – prune old entries periodically
159
+ if (this.processedRetries.size > 1000) {
160
+ const entries = Array.from(this.processedRetries);
161
+ this.processedRetries = new Set(entries.slice(-500));
162
+ }
163
+ return false;
164
+ }
165
+ async resolveUserName(userId) {
166
+ const cached = this.userCache.get(userId);
167
+ if (cached)
168
+ return cached;
169
+ try {
170
+ const result = await this.app.client.users.info({ user: userId });
171
+ const name = result.user?.profile?.display_name ||
172
+ result.user?.real_name ||
173
+ result.user?.name ||
174
+ userId;
175
+ this.userCache.set(userId, name);
176
+ return name;
177
+ }
178
+ catch {
179
+ return userId;
180
+ }
181
+ }
182
+ async slackEventToChatMessage(event) {
183
+ const isBot = !!(event.bot_id || event.user === this.botUserId);
184
+ const userName = isBot
185
+ ? 'bot'
186
+ : await this.resolveUserName(event.user ?? '');
187
+ return {
188
+ platform: 'slack',
189
+ channelId: event.channel,
190
+ conversationId: event.thread_ts ?? event.ts,
191
+ messageId: event.ts,
192
+ userId: event.user ?? event.bot_id ?? '',
193
+ userName,
194
+ text: slackMrkdwnToMarkdown(event.text ?? ''),
195
+ timestamp: event.ts,
196
+ isBot,
197
+ };
198
+ }
199
+ async slackMsgToChatMessage(channelId, msg) {
200
+ const isBot = !!(msg.bot_id || msg.user === this.botUserId);
201
+ const userName = isBot
202
+ ? 'bot'
203
+ : await this.resolveUserName(msg.user ?? '');
204
+ return {
205
+ platform: 'slack',
206
+ channelId,
207
+ conversationId: msg.thread_ts ?? msg.ts,
208
+ messageId: msg.ts,
209
+ userId: msg.user ?? msg.bot_id ?? '',
210
+ userName,
211
+ text: slackMrkdwnToMarkdown(msg.text ?? ''),
212
+ timestamp: msg.ts,
213
+ isBot,
214
+ };
215
+ }
216
+ attemptReconnect() {
217
+ this.reconnectBackoff = Math.min(this.reconnectBackoff * 2, 30_000);
218
+ setTimeout(async () => {
219
+ try {
220
+ await this.app.stop();
221
+ await this.app.start();
222
+ this.reconnectBackoff = 1000;
223
+ this.lastEventTime = Date.now();
224
+ }
225
+ catch {
226
+ // Will retry on next health check interval
227
+ }
228
+ }, this.reconnectBackoff);
229
+ }
230
+ }
231
+ // ── Slack mrkdwn → Markdown conversion ──
232
+ function slackMrkdwnToMarkdown(text) {
233
+ return text
234
+ .replace(/(?<!\*)\*([^*]+)\*(?!\*)/g, '**$1**') // *bold* → **bold**
235
+ .replace(/(?<!_)_([^_]+)_(?!_)/g, '*$1*') // _italic_ → *italic*
236
+ .replace(/~([^~]+)~/g, '~~$1~~') // ~strike~ → ~~strike~~
237
+ .replace(/<(https?:\/\/[^|>]+)\|([^>]+)>/g, '[$2]($1)') // <url|text> → [text](url)
238
+ .replace(/<(https?:\/\/[^>]+)>/g, '$1'); // <url> → url
239
+ }
240
+ //# sourceMappingURL=slack.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slack.js","sourceRoot":"","sources":["../../../../src/lib/relay/platforms/slack.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ import type { ChatPlatform, ChatMessage, PlatformCapabilities, StreamHandle } from '../platform.js';
2
+ export interface TelegramAdapterConfig {
3
+ botToken: string;
4
+ streamMode: 'edit' | 'chunked' | 'final-only';
5
+ editIntervalMs: number;
6
+ }
7
+ export declare class TelegramAdapter implements ChatPlatform {
8
+ private config;
9
+ readonly name = "telegram";
10
+ readonly capabilities: PlatformCapabilities;
11
+ onMessage: (msg: ChatMessage) => Promise<void>;
12
+ onReaction: (channelId: string, messageId: string, userId: string, emoji: string) => Promise<void>;
13
+ onCommand: (channelId: string, userId: string, command: string, args: string) => Promise<string>;
14
+ private bot;
15
+ private InputFile;
16
+ constructor(config: TelegramAdapterConfig);
17
+ start(): Promise<void>;
18
+ stop(): Promise<void>;
19
+ sendMessage(channelId: string, text: string, threadId?: string): Promise<string>;
20
+ updateMessage(channelId: string, messageId: string, text: string): Promise<void>;
21
+ startStream(channelId: string, threadId?: string): Promise<StreamHandle>;
22
+ uploadFile(channelId: string, content: string, filename: string, threadId?: string): Promise<void>;
23
+ getThreadHistory(_channelId: string, _threadId: string, _afterTs?: string): Promise<ChatMessage[]>;
24
+ private streamEdit;
25
+ private streamChunked;
26
+ private streamFinalOnly;
27
+ private telegramToChatMessage;
28
+ private resolveConversationId;
29
+ }
@@ -0,0 +1,193 @@
1
+ // ── Telegram Platform Adapter ──
2
+ // Implements ChatPlatform via grammy Bot framework.
3
+ export class TelegramAdapter {
4
+ config;
5
+ name = 'telegram';
6
+ capabilities = {
7
+ nativeStreaming: false,
8
+ maxStreamUpdateHz: 0.08,
9
+ supportsReactions: true,
10
+ supportsSlashCommands: false,
11
+ supportsThreads: false,
12
+ maxMessageLength: 4096,
13
+ };
14
+ onMessage = async () => { };
15
+ onReaction = async () => { };
16
+ onCommand = async () => '';
17
+ bot; // grammy Bot – dynamically imported
18
+ InputFile; // grammy InputFile constructor
19
+ constructor(config) {
20
+ this.config = config;
21
+ }
22
+ async start() {
23
+ const grammy = await import('grammy');
24
+ const { Bot } = grammy;
25
+ this.InputFile = grammy.InputFile;
26
+ this.bot = new Bot(this.config.botToken);
27
+ // ── Text messages ──
28
+ this.bot.on('message:text', async (ctx) => {
29
+ const msg = ctx.message;
30
+ // Handle /command messages
31
+ if (msg.text.startsWith('/')) {
32
+ const [cmdRaw, ...rest] = msg.text.split(' ');
33
+ const command = cmdRaw.slice(1).replace(/@.*$/, ''); // strip /prefix and @botname
34
+ const args = rest.join(' ');
35
+ const response = await this.onCommand(String(msg.chat.id), String(msg.from.id), command, args);
36
+ if (response) {
37
+ await ctx.reply(response);
38
+ }
39
+ return;
40
+ }
41
+ const chatMessage = this.telegramToChatMessage(msg);
42
+ await this.onMessage(chatMessage);
43
+ });
44
+ // ── Callback queries (inline keyboard feedback) ──
45
+ this.bot.on('callback_query:data', async (ctx) => {
46
+ const query = ctx.callbackQuery;
47
+ await ctx.answerCallbackQuery();
48
+ await this.onReaction(String(query.message?.chat?.id ?? ''), String(query.message?.message_id ?? ''), String(query.from.id), query.data);
49
+ });
50
+ await this.bot.start();
51
+ }
52
+ async stop() {
53
+ if (this.bot) {
54
+ this.bot.stop();
55
+ }
56
+ }
57
+ async sendMessage(channelId, text, threadId) {
58
+ const opts = {};
59
+ if (threadId) {
60
+ opts.reply_to_message_id = parseInt(threadId, 10);
61
+ }
62
+ const escaped = escapeMarkdownV2(text);
63
+ const result = await this.bot.api.sendMessage(channelId, escaped, { ...opts, parse_mode: 'MarkdownV2' });
64
+ return String(result.message_id);
65
+ }
66
+ async updateMessage(channelId, messageId, text) {
67
+ const escaped = escapeMarkdownV2(text);
68
+ await this.bot.api.editMessageText(channelId, parseInt(messageId, 10), escaped, { parse_mode: 'MarkdownV2' });
69
+ }
70
+ async startStream(channelId, threadId) {
71
+ const mode = this.config.streamMode;
72
+ const intervalMs = this.config.editIntervalMs;
73
+ if (mode === 'final-only') {
74
+ return this.streamFinalOnly(channelId, threadId);
75
+ }
76
+ if (mode === 'chunked') {
77
+ return this.streamChunked(channelId, threadId);
78
+ }
79
+ // Default: 'edit' mode
80
+ return this.streamEdit(channelId, threadId, intervalMs);
81
+ }
82
+ async uploadFile(channelId, content, filename, threadId) {
83
+ const opts = {};
84
+ if (threadId) {
85
+ opts.reply_to_message_id = parseInt(threadId, 10);
86
+ }
87
+ const inputFile = new this.InputFile(Buffer.from(content), filename);
88
+ await this.bot.api.sendDocument(channelId, inputFile, opts);
89
+ }
90
+ async getThreadHistory(_channelId, _threadId, _afterTs) {
91
+ // Telegram has no thread history API – rely on SQLite cache
92
+ return [];
93
+ }
94
+ // ── Stream mode implementations ──
95
+ async streamEdit(channelId, threadId, intervalMs) {
96
+ const msgId = await this.sendMessage(channelId, 'Working on it\u2026', threadId);
97
+ let accumulated = '';
98
+ let lastUpdate = 0;
99
+ let pending;
100
+ const flush = async (text) => {
101
+ const now = Date.now();
102
+ if (now - lastUpdate < intervalMs)
103
+ return;
104
+ lastUpdate = now;
105
+ try {
106
+ await this.updateMessage(channelId, msgId, text);
107
+ }
108
+ catch {
109
+ // Telegram rate limit or identical content – ignore
110
+ }
111
+ };
112
+ return {
113
+ append: async (text) => {
114
+ accumulated += text;
115
+ if (pending)
116
+ clearTimeout(pending);
117
+ pending = setTimeout(() => {
118
+ void flush(accumulated);
119
+ }, intervalMs);
120
+ },
121
+ stop: async (finalText) => {
122
+ if (pending)
123
+ clearTimeout(pending);
124
+ await this.updateMessage(channelId, msgId, finalText ?? accumulated);
125
+ },
126
+ };
127
+ }
128
+ async streamChunked(channelId, threadId) {
129
+ const chunkSize = 2000;
130
+ let accumulated = '';
131
+ let sentLength = 0;
132
+ return {
133
+ append: async (text) => {
134
+ accumulated += text;
135
+ while (accumulated.length - sentLength >= chunkSize) {
136
+ const chunk = accumulated.slice(sentLength, sentLength + chunkSize);
137
+ await this.sendMessage(channelId, chunk, threadId);
138
+ sentLength += chunkSize;
139
+ }
140
+ },
141
+ stop: async (finalText) => {
142
+ const remaining = finalText ?? accumulated.slice(sentLength);
143
+ if (remaining.length > 0) {
144
+ await this.sendMessage(channelId, remaining, threadId);
145
+ }
146
+ },
147
+ };
148
+ }
149
+ streamFinalOnly(channelId, threadId) {
150
+ let accumulated = '';
151
+ return {
152
+ append: async (text) => {
153
+ accumulated += text;
154
+ },
155
+ stop: async (finalText) => {
156
+ const content = finalText ?? accumulated;
157
+ if (content.length > 0) {
158
+ await this.sendMessage(channelId, content, threadId);
159
+ }
160
+ },
161
+ };
162
+ }
163
+ // ── Helpers ──
164
+ telegramToChatMessage(msg) {
165
+ const conversationId = this.resolveConversationId(msg);
166
+ return {
167
+ platform: 'telegram',
168
+ channelId: String(msg.chat.id),
169
+ conversationId,
170
+ messageId: String(msg.message_id),
171
+ userId: String(msg.from.id),
172
+ userName: msg.from.username ||
173
+ [msg.from.first_name, msg.from.last_name].filter(Boolean).join(' ') ||
174
+ String(msg.from.id),
175
+ text: msg.text ?? '',
176
+ timestamp: String(msg.date),
177
+ isBot: msg.from.is_bot ?? false,
178
+ };
179
+ }
180
+ resolveConversationId(msg) {
181
+ // Walk reply chain to find root message ID
182
+ let current = msg;
183
+ while (current.reply_to_message) {
184
+ current = current.reply_to_message;
185
+ }
186
+ return String(current.message_id);
187
+ }
188
+ }
189
+ // ── Telegram MarkdownV2 escaping ──
190
+ function escapeMarkdownV2(text) {
191
+ return text.replace(/([_*\[\]()~`>#+\-=|{}.!\\])/g, '\\$1');
192
+ }
193
+ //# sourceMappingURL=telegram.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"telegram.js","sourceRoot":"","sources":["../../../../src/lib/relay/platforms/telegram.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ import type { RelayRunResult } from './types.js';
2
+ /**
3
+ * Split a long response into parts that fit within maxLen.
4
+ * Splits at paragraph boundaries first, then line boundaries, then hard-splits.
5
+ * Numbers parts "[1/N]", "[2/N]" when there are multiple.
6
+ */
7
+ export declare function formatResponse(text: string, maxLen: number): string[];
8
+ /**
9
+ * Build a cost/usage footer for a relay run result.
10
+ */
11
+ export declare function addCostFooter(result: RelayRunResult): string;
12
+ /**
13
+ * Run `git diff --stat` in a project directory.
14
+ * Returns the output or null if not a git repo / no changes.
15
+ */
16
+ export declare function getGitDiffSummary(projectDir: string): string | null;
17
+ /**
18
+ * Multi-stage sanitization of agent output before posting to chat.
19
+ *
20
+ * Stage 1: Pattern-based secret detection (redact matched patterns)
21
+ * Stage 2: Literal env value replacement
22
+ * Stage 3: Length cap
23
+ */
24
+ export declare function sanitizeAgentOutput(text: string, projectEnv: Record<string, string>): string;
@@ -0,0 +1,136 @@
1
+ import { execFileSync } from 'node:child_process';
2
+ const MAX_OUTPUT_CHARS = 40_000;
3
+ const TRUNCATION_THRESHOLD = 12_000;
4
+ /**
5
+ * Split a long response into parts that fit within maxLen.
6
+ * Splits at paragraph boundaries first, then line boundaries, then hard-splits.
7
+ * Numbers parts "[1/N]", "[2/N]" when there are multiple.
8
+ */
9
+ export function formatResponse(text, maxLen) {
10
+ if (text.length > TRUNCATION_THRESHOLD) {
11
+ text = text.slice(0, TRUNCATION_THRESHOLD) +
12
+ '\n\n_Response truncated. Full output available as file._';
13
+ }
14
+ if (text.length <= maxLen) {
15
+ return [text];
16
+ }
17
+ const chunks = [];
18
+ // Split at paragraph boundaries (double newline)
19
+ const paragraphs = text.split(/\n\n+/);
20
+ let current = '';
21
+ for (const para of paragraphs) {
22
+ if (para.length > maxLen) {
23
+ // Flush current buffer
24
+ if (current.length > 0) {
25
+ chunks.push(current.trimEnd());
26
+ current = '';
27
+ }
28
+ // Split long paragraph at line boundaries
29
+ const lines = para.split('\n');
30
+ for (const line of lines) {
31
+ if (line.length > maxLen) {
32
+ // Flush current
33
+ if (current.length > 0) {
34
+ chunks.push(current.trimEnd());
35
+ current = '';
36
+ }
37
+ // Hard-split
38
+ for (let i = 0; i < line.length; i += maxLen) {
39
+ chunks.push(line.slice(i, i + maxLen));
40
+ }
41
+ }
42
+ else if (current.length + line.length + 1 > maxLen) {
43
+ chunks.push(current.trimEnd());
44
+ current = line;
45
+ }
46
+ else {
47
+ current += (current.length > 0 ? '\n' : '') + line;
48
+ }
49
+ }
50
+ }
51
+ else if (current.length + para.length + 2 > maxLen) {
52
+ chunks.push(current.trimEnd());
53
+ current = para;
54
+ }
55
+ else {
56
+ current += (current.length > 0 ? '\n\n' : '') + para;
57
+ }
58
+ }
59
+ if (current.length > 0) {
60
+ chunks.push(current.trimEnd());
61
+ }
62
+ // Number parts if multiple
63
+ if (chunks.length > 1) {
64
+ const total = chunks.length;
65
+ return chunks.map((c, i) => `[${i + 1}/${total}]\n${c}`);
66
+ }
67
+ return chunks;
68
+ }
69
+ /**
70
+ * Build a cost/usage footer for a relay run result.
71
+ */
72
+ export function addCostFooter(result) {
73
+ const dur = Math.round(result.durationMs / 1000);
74
+ const cost = result.costUsd.toFixed(4);
75
+ const model = result.model ?? 'unknown';
76
+ return `_Model: ${model} | Cost: $${cost} | Tokens: ${result.inputTokens} in / ${result.outputTokens} out | Duration: ${dur}s | Turns: ${result.numTurns}_`;
77
+ }
78
+ /**
79
+ * Run `git diff --stat` in a project directory.
80
+ * Returns the output or null if not a git repo / no changes.
81
+ */
82
+ export function getGitDiffSummary(projectDir) {
83
+ try {
84
+ const diff = execFileSync('git', ['diff', '--stat'], {
85
+ cwd: projectDir,
86
+ timeout: 5000,
87
+ encoding: 'utf8',
88
+ stdio: ['pipe', 'pipe', 'pipe'],
89
+ }).trim();
90
+ return diff.length > 0 ? diff : null;
91
+ }
92
+ catch {
93
+ return null;
94
+ }
95
+ }
96
+ /**
97
+ * Multi-stage sanitization of agent output before posting to chat.
98
+ *
99
+ * Stage 1: Pattern-based secret detection (redact matched patterns)
100
+ * Stage 2: Literal env value replacement
101
+ * Stage 3: Length cap
102
+ */
103
+ export function sanitizeAgentOutput(text, projectEnv) {
104
+ let sanitized = text;
105
+ // Stage 1: Pattern-based secret detection — always run all patterns
106
+ const patterns = [
107
+ /(?:sk|pk|api|key|token|secret|password|auth)[-_]?[a-zA-Z0-9]{20,}/gi,
108
+ /AIza[0-9A-Za-z_-]{35}/g,
109
+ /ghp_[0-9a-zA-Z]{36}/g,
110
+ /(?:AKIA|ASIA)[0-9A-Z]{16}/g,
111
+ /xox[bporas]-[0-9a-zA-Z-]{10,}/g,
112
+ /-----BEGIN (?:RSA |EC )?PRIVATE KEY-----/g,
113
+ /sk-ant-[a-zA-Z0-9_-]{20,}/g,
114
+ /eyJ[A-Za-z0-9_-]+\.eyJ[A-Za-z0-9_-]+/g,
115
+ /[a-z]+:\/\/[^:]+:[^@]+@[^\s]+/g,
116
+ /xapp-[0-9a-zA-Z-]{10,}/g,
117
+ ];
118
+ for (const pattern of patterns) {
119
+ sanitized = sanitized.replace(pattern, '[REDACTED]');
120
+ }
121
+ // Stage 2: Literal env value replacement
122
+ for (const [key, value] of Object.entries(projectEnv)) {
123
+ if (value.length > 3) {
124
+ // Use split/join for literal replacement (no regex special chars issues)
125
+ while (sanitized.includes(value)) {
126
+ sanitized = sanitized.split(value).join(`[ENV:${key}]`);
127
+ }
128
+ }
129
+ }
130
+ // Stage 3: Length cap
131
+ if (sanitized.length > MAX_OUTPUT_CHARS) {
132
+ sanitized = sanitized.slice(0, MAX_OUTPUT_CHARS) + '\n[output truncated]';
133
+ }
134
+ return sanitized;
135
+ }
136
+ //# sourceMappingURL=poster.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"poster.js","sourceRoot":"","sources":["../../../src/lib/relay/poster.ts"],"names":[],"mappings":"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"}