@moikapy/lich 0.7.1 → 0.9.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.
- package/CHANGELOG.md +63 -0
- package/README.md +38 -19
- package/dist/{chunk-CVX7LZWC.js → chunk-4N3S6ACI.js} +2 -2
- package/dist/{chunk-WNFBIX4E.js → chunk-KOEZQUIX.js} +1312 -600
- package/dist/chunk-KOEZQUIX.js.map +1 -0
- package/dist/{chunk-PZNYVGD4.js → chunk-P2YEHNM4.js} +2 -2
- package/dist/chunk-P2YEHNM4.js.map +1 -0
- package/dist/chunk-PL6MKRKE.js +75 -0
- package/dist/chunk-PL6MKRKE.js.map +1 -0
- package/dist/chunk-TYRKVBWQ.js +104 -0
- package/dist/chunk-TYRKVBWQ.js.map +1 -0
- package/dist/cli.d.ts +19 -1
- package/dist/cli.js +125 -47
- package/dist/cli.js.map +1 -1
- package/dist/{gateway-44QTIJTJ.js → gateway-OSB7254F.js} +209 -46
- package/dist/gateway-OSB7254F.js.map +1 -0
- package/dist/index.d.ts +122 -62
- package/dist/index.js +12 -4
- package/dist/resolve-H22TOVB5.js +7 -0
- package/dist/resolve-H22TOVB5.js.map +1 -0
- package/dist/store-YYLEB7JG.js +9 -0
- package/dist/store-YYLEB7JG.js.map +1 -0
- package/dist/{tui-MEJGEILU.js → tui-XXIU4W7K.js} +216 -25
- package/dist/tui-XXIU4W7K.js.map +1 -0
- package/docs/architecture/agent-loop.md +28 -9
- package/docs/architecture/overview.md +10 -9
- package/docs/architecture/tools.md +3 -1
- package/docs/getting-started.md +6 -6
- package/docs/index.md +1 -1
- package/docs/user-guide/cli.md +17 -8
- package/docs/user-guide/games.md +5 -4
- package/docs/user-guide/library.md +6 -3
- package/docs/user-guide/redot.md +2 -2
- package/docs/user-guide/tui.md +3 -2
- package/package.json +1 -1
- package/dist/chunk-PZNYVGD4.js.map +0 -1
- package/dist/chunk-WNFBIX4E.js.map +0 -1
- package/dist/gateway-44QTIJTJ.js.map +0 -1
- package/dist/tui-MEJGEILU.js.map +0 -1
- /package/dist/{chunk-CVX7LZWC.js.map → chunk-4N3S6ACI.js.map} +0 -0
|
@@ -6,7 +6,8 @@ import {
|
|
|
6
6
|
platform_token_env,
|
|
7
7
|
read_platform_token,
|
|
8
8
|
sleep
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-KOEZQUIX.js";
|
|
10
|
+
import "./chunk-TYRKVBWQ.js";
|
|
10
11
|
|
|
11
12
|
// src/gateway/types.ts
|
|
12
13
|
var ERROR_SNIPPET_CHARS = 300;
|
|
@@ -73,15 +74,24 @@ var GatewayBus = class {
|
|
|
73
74
|
const key = conversation_key(platform, chat_id);
|
|
74
75
|
const previous = this.chains.get(key) ?? Promise.resolve();
|
|
75
76
|
const run = previous.then(() => this.run_once(key, platform, chat_id, user_id, text));
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
()
|
|
80
|
-
|
|
81
|
-
)
|
|
77
|
+
let tracked = Promise.resolve();
|
|
78
|
+
tracked = run.then(
|
|
79
|
+
() => {
|
|
80
|
+
this.release_chain(key, tracked);
|
|
81
|
+
},
|
|
82
|
+
() => {
|
|
83
|
+
this.release_chain(key, tracked);
|
|
84
|
+
}
|
|
82
85
|
);
|
|
86
|
+
this.chains.set(key, tracked);
|
|
83
87
|
return run;
|
|
84
88
|
}
|
|
89
|
+
/** Drops a settled chain entry unless a newer message re-queued the key. */
|
|
90
|
+
release_chain(key, tracked) {
|
|
91
|
+
if (this.chains.get(key) === tracked) {
|
|
92
|
+
this.chains.delete(key);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
85
95
|
/** Unsubscribes the debug tool logger (bus owns no other resources). */
|
|
86
96
|
stop() {
|
|
87
97
|
this.stop_logging?.();
|
|
@@ -149,7 +159,9 @@ function final_reply_text(content) {
|
|
|
149
159
|
// src/gateway/discord.ts
|
|
150
160
|
var DISCORD_API = "https://discord.com/api/v10";
|
|
151
161
|
var GATEWAY_URL = "wss://gateway.discord.gg/?v=10&encoding=json";
|
|
152
|
-
var INTENTS = 512 | 32768;
|
|
162
|
+
var INTENTS = 512 | 32768 | 4096;
|
|
163
|
+
var DISCORD_BACKOFF_MS = [5e3, 1e4, 2e4, 3e4];
|
|
164
|
+
var DISCORD_FATAL_CLOSE = /* @__PURE__ */ new Set([4004, 4010, 4011, 4012, 4013, 4014]);
|
|
153
165
|
var heartbeat_timers = /* @__PURE__ */ new WeakMap();
|
|
154
166
|
function create_discord_adapter(params) {
|
|
155
167
|
const token = read_platform_token(params.config, "discord");
|
|
@@ -158,69 +170,129 @@ function create_discord_adapter(params) {
|
|
|
158
170
|
}
|
|
159
171
|
let running = false;
|
|
160
172
|
let socket;
|
|
173
|
+
let stop_controller = new AbortController();
|
|
161
174
|
return {
|
|
162
175
|
name: "discord",
|
|
163
176
|
start: async () => {
|
|
164
177
|
running = true;
|
|
178
|
+
stop_controller = new AbortController();
|
|
165
179
|
void connect_loop(params, token, () => running, (opened) => {
|
|
166
180
|
socket = opened;
|
|
167
|
-
});
|
|
181
|
+
}, stop_controller.signal);
|
|
168
182
|
},
|
|
169
183
|
stop: async () => {
|
|
170
184
|
running = false;
|
|
185
|
+
stop_controller.abort();
|
|
171
186
|
socket?.close();
|
|
172
187
|
socket = void 0;
|
|
173
188
|
}
|
|
174
189
|
};
|
|
175
190
|
}
|
|
176
|
-
async function connect_loop(params, token, keep_running, set_socket) {
|
|
191
|
+
async function connect_loop(params, token, keep_running, set_socket, signal) {
|
|
192
|
+
let backoff_index = 0;
|
|
177
193
|
while (keep_running()) {
|
|
194
|
+
let close_code;
|
|
195
|
+
let identified = false;
|
|
178
196
|
try {
|
|
179
197
|
const socket = await open_socket(GATEWAY_URL);
|
|
180
198
|
set_socket(socket);
|
|
181
|
-
await socket_session(socket, token, params);
|
|
199
|
+
const session = await socket_session(socket, token, params);
|
|
200
|
+
close_code = session.close_code;
|
|
201
|
+
identified = session.identified;
|
|
202
|
+
if (close_code !== void 0 && DISCORD_FATAL_CLOSE.has(close_code)) {
|
|
203
|
+
logger.warn(`gateway discord fatal close ${close_code}; stopping reconnect`);
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
182
206
|
} catch (error) {
|
|
183
|
-
logger.warn("gateway discord connection failed
|
|
184
|
-
|
|
207
|
+
logger.warn("gateway discord connection failed", error);
|
|
208
|
+
}
|
|
209
|
+
if (keep_running() === false) {
|
|
210
|
+
return;
|
|
185
211
|
}
|
|
212
|
+
if (identified === true) {
|
|
213
|
+
backoff_index = 0;
|
|
214
|
+
}
|
|
215
|
+
const delay = DISCORD_BACKOFF_MS[backoff_index] ?? 3e4;
|
|
216
|
+
const close_note = close_code === void 0 ? "" : ` (close ${close_code})`;
|
|
217
|
+
logger.warn(`gateway discord reconnecting in ${delay}ms${close_note}`);
|
|
218
|
+
try {
|
|
219
|
+
await sleep(delay, signal);
|
|
220
|
+
} catch {
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
backoff_index = Math.min(backoff_index + 1, DISCORD_BACKOFF_MS.length - 1);
|
|
186
224
|
}
|
|
187
225
|
}
|
|
188
226
|
async function socket_session(socket, token, params) {
|
|
189
|
-
|
|
227
|
+
const state = { last_seq: null, identified: false, ack_pending: false };
|
|
190
228
|
const done = new Promise((resolve) => {
|
|
191
|
-
socket.onclose = () =>
|
|
229
|
+
socket.onclose = ((event) => {
|
|
230
|
+
resolve(typeof event?.code === "number" ? event.code : 1e3);
|
|
231
|
+
});
|
|
192
232
|
});
|
|
193
233
|
socket.onmessage = (event) => {
|
|
194
234
|
const payload = parse_payload(event.data);
|
|
195
235
|
if (payload === void 0) {
|
|
196
236
|
return;
|
|
197
237
|
}
|
|
198
|
-
handle_discord_payload(socket, token, params, payload);
|
|
238
|
+
handle_discord_payload(socket, token, params, payload, state);
|
|
199
239
|
};
|
|
200
|
-
await done;
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
clearInterval(heartbeat);
|
|
204
|
-
heartbeat_timers.delete(socket);
|
|
205
|
-
}
|
|
240
|
+
const close_code = await done;
|
|
241
|
+
clear_heartbeat(socket);
|
|
242
|
+
return { close_code, identified: state.identified };
|
|
206
243
|
}
|
|
207
|
-
function handle_discord_payload(socket, token, params, payload) {
|
|
244
|
+
function handle_discord_payload(socket, token, params, payload, state) {
|
|
245
|
+
if (typeof payload.s === "number") {
|
|
246
|
+
state.last_seq = payload.s;
|
|
247
|
+
}
|
|
248
|
+
if (payload.op === 11) {
|
|
249
|
+
state.ack_pending = false;
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
if (payload.op === 1) {
|
|
253
|
+
send_heartbeat(socket, state);
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
if (payload.op === 7 || payload.op === 9) {
|
|
257
|
+
socket.close();
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
208
260
|
if (payload.op === 10 && is_hello(payload.d)) {
|
|
209
261
|
socket.send(JSON.stringify({ op: 2, d: identify_body(token) }));
|
|
210
|
-
schedule_heartbeat(socket, payload.d.heartbeat_interval);
|
|
262
|
+
schedule_heartbeat(socket, payload.d.heartbeat_interval, state);
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
if (payload.t === "READY") {
|
|
266
|
+
state.identified = true;
|
|
211
267
|
return;
|
|
212
268
|
}
|
|
213
269
|
if (payload.t === "MESSAGE_CREATE") {
|
|
214
270
|
void on_message_create(params, payload.d);
|
|
215
271
|
}
|
|
216
272
|
}
|
|
217
|
-
function schedule_heartbeat(socket, interval_ms) {
|
|
273
|
+
function schedule_heartbeat(socket, interval_ms, state) {
|
|
274
|
+
clear_heartbeat(socket);
|
|
218
275
|
const interval = typeof interval_ms === "number" ? interval_ms : 45e3;
|
|
219
276
|
const timer = setInterval(() => {
|
|
220
|
-
|
|
277
|
+
if (state.ack_pending === true) {
|
|
278
|
+
socket.close();
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
send_heartbeat(socket, state);
|
|
221
282
|
}, Math.max(1e3, interval - 1e3));
|
|
222
283
|
heartbeat_timers.set(socket, timer);
|
|
223
284
|
}
|
|
285
|
+
function send_heartbeat(socket, state) {
|
|
286
|
+
state.ack_pending = true;
|
|
287
|
+
socket.send(JSON.stringify({ op: 1, d: state.last_seq }));
|
|
288
|
+
}
|
|
289
|
+
function clear_heartbeat(socket) {
|
|
290
|
+
const heartbeat = heartbeat_timers.get(socket);
|
|
291
|
+
if (heartbeat !== void 0) {
|
|
292
|
+
clearInterval(heartbeat);
|
|
293
|
+
heartbeat_timers.delete(socket);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
224
296
|
function identify_body(token) {
|
|
225
297
|
return { token, intents: INTENTS, properties: { os: "linux", browser: "lich", device: "lich" } };
|
|
226
298
|
}
|
|
@@ -254,7 +326,7 @@ async function rest_send_message(params, channel_id, text) {
|
|
|
254
326
|
const response = await fetch(`${DISCORD_API}/channels/${channel_id}/messages`, {
|
|
255
327
|
method: "POST",
|
|
256
328
|
headers: { authorization: `Bot ${token}`, "content-type": "application/json" },
|
|
257
|
-
body: JSON.stringify({ content: chunk }),
|
|
329
|
+
body: JSON.stringify({ content: chunk, allowed_mentions: { parse: [] } }),
|
|
258
330
|
signal: AbortSignal.timeout(3e4)
|
|
259
331
|
});
|
|
260
332
|
if (response.ok === false) {
|
|
@@ -423,7 +495,9 @@ function split_text(text, limit) {
|
|
|
423
495
|
|
|
424
496
|
// src/gateway/twitch.ts
|
|
425
497
|
var TWITCH_IRC_URL = "wss://irc-ws.chat.twitch.tv:443";
|
|
426
|
-
var TWITCH_MESSAGE_CAP =
|
|
498
|
+
var TWITCH_MESSAGE_CAP = 450;
|
|
499
|
+
var TWITCH_BACKOFF_MS = [5e3, 1e4, 2e4, 3e4];
|
|
500
|
+
var TWITCH_CHUNK_GAP_MS = 1600;
|
|
427
501
|
function create_twitch_adapter(params) {
|
|
428
502
|
const twitch = read_twitch_env(params.config);
|
|
429
503
|
if (twitch === void 0) {
|
|
@@ -433,16 +507,19 @@ function create_twitch_adapter(params) {
|
|
|
433
507
|
}
|
|
434
508
|
let running = false;
|
|
435
509
|
let socket;
|
|
510
|
+
let stop_controller = new AbortController();
|
|
436
511
|
return {
|
|
437
512
|
name: "twitch",
|
|
438
513
|
start: async () => {
|
|
439
514
|
running = true;
|
|
515
|
+
stop_controller = new AbortController();
|
|
440
516
|
void irc_loop(params, twitch, () => running, (opened) => {
|
|
441
517
|
socket = opened;
|
|
442
|
-
});
|
|
518
|
+
}, stop_controller.signal);
|
|
443
519
|
},
|
|
444
520
|
stop: async () => {
|
|
445
521
|
running = false;
|
|
522
|
+
stop_controller.abort();
|
|
446
523
|
socket?.close();
|
|
447
524
|
socket = void 0;
|
|
448
525
|
}
|
|
@@ -462,25 +539,51 @@ function read_twitch_env(config) {
|
|
|
462
539
|
}
|
|
463
540
|
return { token, nick, channels };
|
|
464
541
|
}
|
|
465
|
-
async function irc_loop(params, twitch, keep_running, set_socket) {
|
|
542
|
+
async function irc_loop(params, twitch, keep_running, set_socket, signal) {
|
|
543
|
+
let backoff_index = 0;
|
|
466
544
|
while (keep_running()) {
|
|
545
|
+
let close_code;
|
|
546
|
+
let welcomed = false;
|
|
467
547
|
try {
|
|
468
548
|
const socket = await open_socket(TWITCH_IRC_URL);
|
|
469
549
|
set_socket(socket);
|
|
470
|
-
await irc_session(params, twitch, socket
|
|
550
|
+
const session = await irc_session(params, twitch, socket);
|
|
551
|
+
close_code = session.close_code;
|
|
552
|
+
welcomed = session.welcomed;
|
|
471
553
|
} catch (error) {
|
|
472
|
-
logger.warn("gateway twitch connection failed
|
|
473
|
-
|
|
554
|
+
logger.warn("gateway twitch connection failed", error);
|
|
555
|
+
}
|
|
556
|
+
if (keep_running() === false) {
|
|
557
|
+
return;
|
|
558
|
+
}
|
|
559
|
+
if (welcomed === true) {
|
|
560
|
+
backoff_index = 0;
|
|
561
|
+
}
|
|
562
|
+
const delay = TWITCH_BACKOFF_MS[backoff_index] ?? 3e4;
|
|
563
|
+
const close_note = close_code === void 0 ? "" : ` (close ${close_code})`;
|
|
564
|
+
logger.warn(`gateway twitch reconnecting in ${delay}ms${close_note}`);
|
|
565
|
+
try {
|
|
566
|
+
await sleep(delay, signal);
|
|
567
|
+
} catch {
|
|
568
|
+
return;
|
|
474
569
|
}
|
|
570
|
+
backoff_index = Math.min(backoff_index + 1, TWITCH_BACKOFF_MS.length - 1);
|
|
475
571
|
}
|
|
476
572
|
}
|
|
477
|
-
async function irc_session(params, twitch, socket
|
|
573
|
+
async function irc_session(params, twitch, socket) {
|
|
574
|
+
let welcomed = false;
|
|
478
575
|
const closed = new Promise((resolve) => {
|
|
479
|
-
socket.onclose = () =>
|
|
576
|
+
socket.onclose = ((event) => {
|
|
577
|
+
resolve(typeof event?.code === "number" ? event.code : 1e3);
|
|
578
|
+
});
|
|
480
579
|
});
|
|
481
580
|
socket.onmessage = (event) => {
|
|
482
581
|
for (const line of String(event.data).split("\r\n")) {
|
|
483
|
-
void handle_irc_line(params, twitch, socket, line)
|
|
582
|
+
void handle_irc_line(params, twitch, socket, line, (ok) => {
|
|
583
|
+
if (ok === true) {
|
|
584
|
+
welcomed = true;
|
|
585
|
+
}
|
|
586
|
+
});
|
|
484
587
|
}
|
|
485
588
|
};
|
|
486
589
|
socket.send("CAP REQ :twitch.tv/tags twitch.tv/commands");
|
|
@@ -489,13 +592,18 @@ async function irc_session(params, twitch, socket, keep_running) {
|
|
|
489
592
|
for (const channel of twitch.channels) {
|
|
490
593
|
socket.send(`JOIN #${channel}`);
|
|
491
594
|
}
|
|
492
|
-
await closed;
|
|
595
|
+
const close_code = await closed;
|
|
596
|
+
return { close_code, welcomed };
|
|
493
597
|
}
|
|
494
|
-
async function handle_irc_line(params, twitch, socket, line) {
|
|
598
|
+
async function handle_irc_line(params, twitch, socket, line, on_welcome) {
|
|
495
599
|
if (line.length === 0) {
|
|
496
600
|
return;
|
|
497
601
|
}
|
|
498
602
|
const parsed = parse_irc_line(line);
|
|
603
|
+
if (parsed.kind === "welcome") {
|
|
604
|
+
on_welcome(true);
|
|
605
|
+
return;
|
|
606
|
+
}
|
|
499
607
|
if (parsed.kind === "ping") {
|
|
500
608
|
socket.send("PONG :tmi.twitch.tv");
|
|
501
609
|
return;
|
|
@@ -510,17 +618,43 @@ async function handle_irc_line(params, twitch, socket, line) {
|
|
|
510
618
|
parsed.user,
|
|
511
619
|
parsed.text
|
|
512
620
|
);
|
|
513
|
-
send_twitch_message(socket, parsed.channel, reply);
|
|
621
|
+
await send_twitch_message(socket, parsed.channel, reply);
|
|
514
622
|
}
|
|
515
|
-
function send_twitch_message(socket, channel, text) {
|
|
516
|
-
|
|
517
|
-
|
|
623
|
+
async function send_twitch_message(socket, channel, text) {
|
|
624
|
+
const safe = sanitize_twitch_outbound(text);
|
|
625
|
+
if (safe.length === 0) {
|
|
626
|
+
return;
|
|
627
|
+
}
|
|
628
|
+
const chunks = split_chunks2(safe, TWITCH_MESSAGE_CAP);
|
|
629
|
+
for (let index = 0; index < chunks.length; index += 1) {
|
|
630
|
+
const chunk = chunks[index];
|
|
631
|
+
if (chunk === void 0) {
|
|
632
|
+
continue;
|
|
633
|
+
}
|
|
634
|
+
const sanitized_chunk = sanitize_twitch_outbound(chunk);
|
|
635
|
+
if (sanitized_chunk.length === 0) {
|
|
636
|
+
continue;
|
|
637
|
+
}
|
|
638
|
+
socket.send(`PRIVMSG #${channel} :${sanitized_chunk}`);
|
|
639
|
+
if (index + 1 < chunks.length) {
|
|
640
|
+
await sleep(TWITCH_CHUNK_GAP_MS);
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
function sanitize_twitch_outbound(text) {
|
|
645
|
+
let cleaned = text.replace(/[\r\n]+/g, " ").trim();
|
|
646
|
+
while (cleaned.startsWith("/") === true || cleaned.startsWith(".") === true) {
|
|
647
|
+
cleaned = cleaned.slice(1).trimStart();
|
|
518
648
|
}
|
|
649
|
+
return cleaned;
|
|
519
650
|
}
|
|
520
651
|
function parse_irc_line(line) {
|
|
521
652
|
if (line.startsWith("PING") === true) {
|
|
522
653
|
return { kind: "ping", channel: "", user: "", text: "" };
|
|
523
654
|
}
|
|
655
|
+
if (/:\S+ 001 /.test(line) === true || line.includes(" GLOBALUSERSTATE ") === true) {
|
|
656
|
+
return { kind: "welcome", channel: "", user: "", text: "" };
|
|
657
|
+
}
|
|
524
658
|
const privmsg = match_privmsg(line);
|
|
525
659
|
if (privmsg === void 0) {
|
|
526
660
|
return { kind: "other", channel: "", user: "", text: "" };
|
|
@@ -572,6 +706,7 @@ function format_agent_reply(text, usage, platform) {
|
|
|
572
706
|
// src/gateway/webhook.ts
|
|
573
707
|
var DEFAULT_GATEWAY_PORT = 8089;
|
|
574
708
|
var DEFAULT_GATEWAY_HOST = "127.0.0.1";
|
|
709
|
+
var MAX_WEBHOOK_BODY_BYTES = 1e6;
|
|
575
710
|
function create_webhook_adapter(params) {
|
|
576
711
|
const port = params.port ?? read_port_env() ?? DEFAULT_GATEWAY_PORT;
|
|
577
712
|
const host = params.host ?? read_host_env() ?? DEFAULT_GATEWAY_HOST;
|
|
@@ -617,7 +752,16 @@ async function dispatch_webhook(params, request, response, token) {
|
|
|
617
752
|
}
|
|
618
753
|
}
|
|
619
754
|
async function handle_message_post(params, request, response) {
|
|
620
|
-
|
|
755
|
+
if (content_length_exceeds(request, MAX_WEBHOOK_BODY_BYTES) === true) {
|
|
756
|
+
request.resume();
|
|
757
|
+
send_json(response, 413, { error: "payload too large" });
|
|
758
|
+
return;
|
|
759
|
+
}
|
|
760
|
+
const body = await read_body(request, MAX_WEBHOOK_BODY_BYTES);
|
|
761
|
+
if (body === void 0) {
|
|
762
|
+
send_json(response, 413, { error: "payload too large" });
|
|
763
|
+
return;
|
|
764
|
+
}
|
|
621
765
|
const payload = parse_payload2(body);
|
|
622
766
|
const text = payload.text;
|
|
623
767
|
if (text === void 0) {
|
|
@@ -630,13 +774,32 @@ async function handle_message_post(params, request, response) {
|
|
|
630
774
|
const reply = await params.handle_message(platform, String(chat_id), String(user_id), String(text));
|
|
631
775
|
respond_json_text(response, 200, format_agent_reply(reply ?? "", void 0, "webhook"));
|
|
632
776
|
}
|
|
633
|
-
function
|
|
777
|
+
function content_length_exceeds(request, max_bytes) {
|
|
778
|
+
const raw = request.headers["content-length"];
|
|
779
|
+
if (raw === void 0) {
|
|
780
|
+
return false;
|
|
781
|
+
}
|
|
782
|
+
const length = Number(raw);
|
|
783
|
+
return Number.isFinite(length) === true && length > max_bytes;
|
|
784
|
+
}
|
|
785
|
+
function read_body(request, max_bytes) {
|
|
634
786
|
return new Promise((resolve, reject) => {
|
|
635
787
|
let body = "";
|
|
788
|
+
let size = 0;
|
|
789
|
+
let too_large = false;
|
|
636
790
|
request.on("data", (chunk) => {
|
|
791
|
+
if (too_large === true) {
|
|
792
|
+
return;
|
|
793
|
+
}
|
|
794
|
+
size += chunk.length;
|
|
795
|
+
if (size > max_bytes) {
|
|
796
|
+
too_large = true;
|
|
797
|
+
body = "";
|
|
798
|
+
return;
|
|
799
|
+
}
|
|
637
800
|
body += chunk.toString("utf8");
|
|
638
801
|
});
|
|
639
|
-
request.on("end", () => resolve(body));
|
|
802
|
+
request.on("end", () => resolve(too_large === true ? void 0 : body));
|
|
640
803
|
request.on("error", reject);
|
|
641
804
|
});
|
|
642
805
|
}
|
|
@@ -817,4 +980,4 @@ function install_signal_handlers(agent, bus, adapters) {
|
|
|
817
980
|
export {
|
|
818
981
|
run_gateway
|
|
819
982
|
};
|
|
820
|
-
//# sourceMappingURL=gateway-
|
|
983
|
+
//# sourceMappingURL=gateway-OSB7254F.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/gateway/types.ts","../src/gateway/bus.ts","../src/gateway/discord.ts","../src/gateway/telegram.ts","../src/gateway/twitch.ts","../src/gateway/webhook.ts","../src/gateway/format.ts","../src/gateway/runner.ts"],"sourcesContent":["/**\n * Shared contracts for the messaging gateway: platform message shapes,\n * reply sinks, adapter lifecycle, and the small runtime helpers every\n * platform adapter shares (idle fallback, raw WebSocket, inbound dispatch).\n */\nimport type { Agent } from \"../agent/agent.js\";\nimport type { AgentConfig } from \"../agent/config.js\";\nimport { logger } from \"../util/log.js\";\n\nexport type PlatformName = \"webhook\" | \"telegram\" | \"discord\" | \"twitch\";\n\n/** Normalized inbound message from any supported platform. */\nexport interface PlatformMessage {\n platform: PlatformName;\n chat_id: string;\n user_id: string;\n text: string;\n}\n\n/** Outbound-only view used to deliver a reply to a conversation. */\nexport interface ReplySink {\n send(text: string): Promise<void>;\n}\n\n/** Lifecycle contract implemented by every platform adapter. */\nexport interface PlatformAdapter {\n readonly name: string;\n start(): Promise<void>;\n stop(): Promise<void>;\n}\n\n/** Handles one inbound message and resolves to the reply text. */\nexport type InboundHandler = (\n platform: string,\n chat_id: string,\n user_id: string,\n text: string,\n) => Promise<string | undefined>;\n\n/** Dependencies handed to every adapter factory. */\nexport interface AdapterParams {\n config: AgentConfig;\n handle_message: InboundHandler;\n get_agent: () => Agent;\n reply_router: (platform: string, chat_id: string) => ReplySink | undefined;\n}\n\n/** Minimal structural WebSocket surface shared by bun and node runtimes. */\nexport interface RawSocket {\n onopen: (() => void) | null;\n onmessage: ((event: { data: unknown }) => void) | null;\n onclose: (() => void) | null;\n onerror: ((event: unknown) => void) | null;\n send(data: string): void;\n close(): void;\n}\n\nconst ERROR_SNIPPET_CHARS = 300;\n\n/** Flattens a failure into a single-line, bounded, safe reply string. */\nexport function sanitize_agent_error(error: unknown): string {\n const raw = error instanceof Error ? error.message : String(error);\n const flat = raw.replace(/\\s+/g, \" \").trim();\n return `agent error: ${flat.slice(0, ERROR_SNIPPET_CHARS) || \"unknown\"}`;\n}\n\n/** Adapter that logs why it is idle once and otherwise does nothing. */\nexport function create_idle_adapter(name: string, reason: string): PlatformAdapter {\n logger.warn(`gateway ${name} adapter idle: ${reason}`);\n return {\n name,\n start: async () => undefined,\n stop: async () => undefined,\n };\n}\n\n/** Opens a WebSocket and resolves once connected; rejects on open error. */\nexport function open_socket(url: string): Promise<RawSocket> {\n return new Promise((resolve, reject) => {\n const ctor = (globalThis as { WebSocket?: new (url: string) => unknown }).WebSocket;\n if (ctor === undefined) {\n reject(new Error(\"runtime does not expose a WebSocket constructor\"));\n return;\n }\n const socket = new ctor(url) as unknown as RawSocket;\n socket.onopen = () => resolve(socket);\n socket.onerror = () => reject(new Error(`websocket connect failed: ${url}`));\n });\n}\n\n/** Runs one inbound message, converting failures into a safe reply string. */\nexport async function run_inbound_message(\n handle: InboundHandler,\n platform: string,\n chat_id: string,\n user_id: string,\n text: string,\n): Promise<string> {\n try {\n return (await handle(platform, chat_id, user_id, text)) ?? \"\";\n } catch (error) {\n logger.error(`gateway ${platform} message handling failed`, error);\n return sanitize_agent_error(error);\n }\n}","/**\n * GatewayBus: conversation-keyed runner over one shared Agent.\n *\n * Per-conversation history lives in a bounded Map; concurrent messages for\n * the same conversation are serialized through a promise chain so history\n * never interleaves. Agent failures become sanitized reply strings.\n */\nimport type { Agent } from \"../agent/agent.js\";\nimport type { AgentConfig } from \"../agent/config.js\";\nimport type { Message } from \"../providers/types.js\";\nimport { logger } from \"../util/log.js\";\nimport { check_gateway_sender } from \"./access.js\";\nimport { sanitize_agent_error } from \"./types.js\";\n\nexport interface GatewayBusOptions {\n history_cap?: number;\n max_conversations?: number;\n}\n\nexport interface BusParams {\n config: AgentConfig;\n agent_factory: () => Agent;\n /** Subscribe to agent tool events for debug logging (creates the agent). */\n wire_tool_logging?: boolean;\n}\n\nconst DEFAULT_HISTORY_CAP = 40;\nconst DEFAULT_MAX_CONVERSATIONS = 200;\n\nexport class GatewayBus {\n private readonly config: AgentConfig;\n private readonly agent_factory: () => Agent;\n private agent: Agent | undefined;\n private readonly histories: Map<string, Message[]> = new Map();\n private readonly chains: Map<string, Promise<void>> = new Map();\n private readonly history_cap: number;\n private readonly max_conversations: number;\n private stop_logging: (() => void) | undefined;\n\n constructor(params: BusParams, options?: GatewayBusOptions) {\n this.config = params.config;\n this.agent_factory = params.agent_factory;\n this.history_cap = options?.history_cap ?? DEFAULT_HISTORY_CAP;\n this.max_conversations = options?.max_conversations ?? DEFAULT_MAX_CONVERSATIONS;\n if (params.wire_tool_logging === true) {\n this.wire_tool_logging();\n }\n }\n\n /** Serializes runs per conversation and resolves to the reply text. */\n async handle(platform: string, chat_id: string, user_id: string, text: string): Promise<string | undefined> {\n if (check_gateway_sender(this.config, platform, chat_id, user_id) === false) {\n return undefined;\n }\n const key = conversation_key(platform, chat_id);\n const previous = this.chains.get(key) ?? Promise.resolve();\n const run = previous.then(() => this.run_once(key, platform, chat_id, user_id, text));\n let tracked: Promise<void> = Promise.resolve();\n tracked = run.then(\n () => {\n this.release_chain(key, tracked);\n },\n () => {\n this.release_chain(key, tracked);\n },\n );\n this.chains.set(key, tracked);\n return run;\n }\n\n /** Drops a settled chain entry unless a newer message re-queued the key. */\n private release_chain(key: string, tracked: Promise<void>): void {\n if (this.chains.get(key) === tracked) {\n this.chains.delete(key);\n }\n }\n\n /** Unsubscribes the debug tool logger (bus owns no other resources). */\n stop(): void {\n this.stop_logging?.();\n this.stop_logging = undefined;\n }\n\n private async run_once(\n key: string,\n platform: string,\n chat_id: string,\n user_id: string,\n text: string,\n ): Promise<string | undefined> {\n const input = text.startsWith(\"/start\") === true ? \"hello\" : text;\n const history = this.history_for(key);\n const agent = this.ensure_agent();\n try {\n const result = await agent.run({ input, history, label: `gw:${platform}:${chat_id}` });\n this.histories.set(key, cap_history(result.messages, this.history_cap));\n return final_reply_text(result.outcome.final?.content);\n } catch (error) {\n logger.error(`gateway bus run failed for ${key} (user ${user_id})`, error);\n return sanitize_agent_error(error);\n }\n }\n\n private ensure_agent(): Agent {\n if (this.agent === undefined) {\n this.agent = this.agent_factory();\n }\n return this.agent;\n }\n\n /** Oldest-first eviction keeps the conversation map bounded. */\n private history_for(key: string): Message[] {\n while (this.histories.size >= this.max_conversations && this.histories.has(key) === false) {\n const oldest = this.histories.keys().next();\n if (oldest.done === true) {\n break;\n }\n this.histories.delete(oldest.value);\n }\n return this.histories.get(key) ?? [];\n }\n\n /** Logs completed tool calls at debug level for gateway observability. */\n private wire_tool_logging(): void {\n const agent = this.agent_factory();\n this.agent = agent;\n this.stop_logging = agent.events.on((event) => {\n if (event.type === \"tool_call_end\") {\n logger.debug(`tool ${event.call.name} ${event.result.ok === true ? \"ok\" : \"failed\"}`);\n }\n });\n }\n}\n\nfunction conversation_key(platform: string, chat_id: string): string {\n return `${platform}:${chat_id}`;\n}\n\nfunction cap_history(messages: Message[], cap: number): Message[] {\n const overflow = messages.length - cap;\n if (overflow <= 0) {\n return messages;\n }\n let sliced = messages.slice(overflow);\n while (sliced.length > 0 && sliced[0]?.role !== \"user\") {\n sliced = sliced.slice(1);\n }\n return sliced;\n}\n\nfunction final_reply_text(content: string | undefined): string | undefined {\n return content === undefined || content.length === 0 ? undefined : content;\n}","/**\n * Discord adapter: gateway WebSocket (op-code switch, heartbeat, fresh\n * reconnect) plus REST replies. Token optional; absent token degrades to\n * an idle adapter. No resume support — reconnects are fresh by design.\n */\nimport { logger } from \"../util/log.js\";\nimport { sleep } from \"../util/sleep.js\";\nimport { platform_token_env, read_platform_token } from \"./token_env.js\";\nimport type { AdapterParams, PlatformAdapter, RawSocket } from \"./types.js\";\nimport { create_idle_adapter, open_socket, run_inbound_message } from \"./types.js\";\n\nconst DISCORD_API = \"https://discord.com/api/v10\";\nconst GATEWAY_URL = \"wss://gateway.discord.gg/?v=10&encoding=json\";\n/** Guild messages + message content + direct messages. */\nconst INTENTS = 512 | 32768 | 4096;\nexport const DISCORD_BACKOFF_MS = [5000, 10000, 20000, 30000] as const;\n/** Auth / sharding / API-version failures — reconnecting cannot recover. */\nconst DISCORD_FATAL_CLOSE = new Set([4004, 4010, 4011, 4012, 4013, 4014]);\n/** Live heartbeat timers keyed by socket, cleared when the session ends. */\nconst heartbeat_timers = new WeakMap<RawSocket, ReturnType<typeof setInterval>>();\n\ntype DiscordPayload = {\n op?: number;\n t?: string;\n s?: number | null;\n d?: Record<string, unknown>;\n};\n\ninterface DiscordMessageData {\n content: string;\n channel_id: string;\n author_id: string;\n}\n\ninterface DiscordSessionState {\n last_seq: number | null;\n identified: boolean;\n ack_pending: boolean;\n}\n\nexport function create_discord_adapter(params: AdapterParams): PlatformAdapter {\n const token = read_platform_token(params.config, \"discord\");\n if (token === undefined) {\n return create_idle_adapter(\"discord\", `${platform_token_env(params.config, \"discord\")} not set`);\n }\n let running = false;\n let socket: RawSocket | undefined;\n let stop_controller = new AbortController();\n return {\n name: \"discord\",\n start: async () => {\n running = true;\n stop_controller = new AbortController();\n void connect_loop(params, token, () => running, (opened) => {\n socket = opened;\n }, stop_controller.signal);\n },\n stop: async () => {\n running = false;\n stop_controller.abort();\n socket?.close();\n socket = undefined;\n },\n };\n}\n\nasync function connect_loop(\n params: AdapterParams,\n token: string,\n keep_running: () => boolean,\n set_socket: (socket: RawSocket) => void,\n signal: AbortSignal,\n): Promise<void> {\n let backoff_index = 0;\n while (keep_running()) {\n let close_code: number | undefined;\n let identified = false;\n try {\n const socket = await open_socket(GATEWAY_URL);\n set_socket(socket);\n const session = await socket_session(socket, token, params);\n close_code = session.close_code;\n identified = session.identified;\n if (close_code !== undefined && DISCORD_FATAL_CLOSE.has(close_code)) {\n logger.warn(`gateway discord fatal close ${close_code}; stopping reconnect`);\n return;\n }\n } catch (error) {\n logger.warn(\"gateway discord connection failed\", error);\n }\n if (keep_running() === false) {\n return;\n }\n if (identified === true) {\n backoff_index = 0;\n }\n const delay = DISCORD_BACKOFF_MS[backoff_index] ?? 30000;\n const close_note = close_code === undefined ? \"\" : ` (close ${close_code})`;\n logger.warn(`gateway discord reconnecting in ${delay}ms${close_note}`);\n try {\n await sleep(delay, signal);\n } catch {\n return;\n }\n backoff_index = Math.min(backoff_index + 1, DISCORD_BACKOFF_MS.length - 1);\n }\n}\n\nasync function socket_session(\n socket: RawSocket,\n token: string,\n params: AdapterParams,\n): Promise<{ close_code: number; identified: boolean }> {\n const state: DiscordSessionState = { last_seq: null, identified: false, ack_pending: false };\n const done = new Promise<number>((resolve) => {\n socket.onclose = ((event?: { code?: number }) => {\n resolve(typeof event?.code === \"number\" ? event.code : 1000);\n }) as () => void;\n });\n socket.onmessage = (event) => {\n const payload = parse_payload(event.data);\n if (payload === undefined) {\n return;\n }\n handle_discord_payload(socket, token, params, payload, state);\n };\n const close_code = await done;\n clear_heartbeat(socket);\n return { close_code, identified: state.identified };\n}\n\nfunction handle_discord_payload(\n socket: RawSocket,\n token: string,\n params: AdapterParams,\n payload: DiscordPayload,\n state: DiscordSessionState,\n): void {\n if (typeof payload.s === \"number\") {\n state.last_seq = payload.s;\n }\n if (payload.op === 11) {\n state.ack_pending = false;\n return;\n }\n if (payload.op === 1) {\n send_heartbeat(socket, state);\n return;\n }\n if (payload.op === 7 || payload.op === 9) {\n socket.close();\n return;\n }\n if (payload.op === 10 && is_hello(payload.d)) {\n socket.send(JSON.stringify({ op: 2, d: identify_body(token) }));\n schedule_heartbeat(socket, payload.d.heartbeat_interval, state);\n return;\n }\n if (payload.t === \"READY\") {\n state.identified = true;\n return;\n }\n if (payload.t === \"MESSAGE_CREATE\") {\n void on_message_create(params, payload.d);\n }\n}\n\nfunction schedule_heartbeat(\n socket: RawSocket,\n interval_ms: unknown,\n state: DiscordSessionState,\n): void {\n clear_heartbeat(socket);\n const interval = typeof interval_ms === \"number\" ? interval_ms : 45000;\n const timer = setInterval(() => {\n if (state.ack_pending === true) {\n socket.close();\n return;\n }\n send_heartbeat(socket, state);\n }, Math.max(1000, interval - 1000));\n heartbeat_timers.set(socket, timer);\n}\n\nfunction send_heartbeat(socket: RawSocket, state: DiscordSessionState): void {\n state.ack_pending = true;\n socket.send(JSON.stringify({ op: 1, d: state.last_seq }));\n}\n\nfunction clear_heartbeat(socket: RawSocket): void {\n const heartbeat = heartbeat_timers.get(socket);\n if (heartbeat !== undefined) {\n clearInterval(heartbeat);\n heartbeat_timers.delete(socket);\n }\n}\n\nfunction identify_body(token: string): Record<string, unknown> {\n return { token, intents: INTENTS, properties: { os: \"linux\", browser: \"lich\", device: \"lich\" } };\n}\n\nasync function on_message_create(params: AdapterParams, data: Record<string, unknown> | undefined): Promise<void> {\n try {\n const message = normalize_message(data);\n if (message === undefined) {\n return;\n }\n const reply = await run_inbound_message(\n params.handle_message,\n \"discord\",\n message.channel_id,\n message.author_id,\n message.content,\n );\n if (reply.length === 0) {\n return;\n }\n await rest_send_message(params, message.channel_id, reply);\n } catch (error) {\n logger.warn(\"gateway discord deliver failed\", error);\n }\n}\n\nasync function rest_send_message(params: AdapterParams, channel_id: string, text: string): Promise<void> {\n const token = read_platform_token(params.config, \"discord\");\n if (token === undefined || channel_id === \"\") {\n return;\n }\n for (const chunk of split_chunks(text, 2000)) {\n const response = await fetch(`${DISCORD_API}/channels/${channel_id}/messages`, {\n method: \"POST\",\n headers: { authorization: `Bot ${token}`, \"content-type\": \"application/json\" },\n body: JSON.stringify({ content: chunk, allowed_mentions: { parse: [] } }),\n signal: AbortSignal.timeout(30_000),\n });\n if (response.ok === false) {\n logger.warn(`gateway discord sendMessage failed with http ${response.status}`);\n }\n }\n}\n\nfunction normalize_message(data: Record<string, unknown> | undefined): DiscordMessageData | undefined {\n if (data === undefined) {\n return undefined;\n }\n const author = data.author;\n if (typeof author !== \"object\" || author === null) {\n return undefined;\n }\n const info = author as { id?: unknown; bot?: unknown };\n if (info.bot === true) {\n return undefined;\n }\n const channel_id = typeof data.channel_id === \"string\" ? data.channel_id : \"\";\n const content = strip_mention(typeof data.content === \"string\" ? data.content : \"\");\n return { channel_id, content, author_id: typeof info.id === \"string\" ? info.id : \"\" };\n}\n\nfunction strip_mention(content: string): string {\n const bot_id = process.env.LICH_DISCORD_BOT_ID;\n if (bot_id === undefined) {\n return content.trim();\n }\n const mention = `<@${bot_id}>`;\n return content.startsWith(mention) === true ? content.slice(mention.length).trim() : content.trim();\n}\n\nfunction is_hello(d: Record<string, unknown> | undefined): d is { heartbeat_interval: number } {\n return d !== undefined && typeof d.heartbeat_interval === \"number\";\n}\n\nfunction parse_payload(data: unknown): DiscordPayload | undefined {\n if (typeof data !== \"string\") {\n return undefined;\n }\n try {\n return JSON.parse(data) as DiscordPayload;\n } catch {\n return undefined;\n }\n}\n\nfunction split_chunks(text: string, limit: number): string[] {\n const chunks: string[] = [];\n let rest = text;\n while (rest.length > limit) {\n chunks.push(rest.slice(0, limit));\n rest = rest.slice(limit);\n }\n if (rest.length > 0) {\n chunks.push(rest);\n }\n return chunks;\n}\n","/**\n * Telegram adapter: long-poll getUpdates over fetch and sendMessage\n * replies with 4096-char splitting. Missing token degrades to an idle\n * adapter instead of failing the gateway.\n */\nimport { sleep } from \"../util/sleep.js\";\nimport { logger } from \"../util/log.js\";\nimport { platform_token_env, read_platform_token } from \"./token_env.js\";\nimport type { AdapterParams, PlatformAdapter } from \"./types.js\";\nimport { create_idle_adapter, run_inbound_message } from \"./types.js\";\n\nexport const TELEGRAM_MAX_MESSAGE_CHARS = 4096;\n\ninterface TelegramUpdate {\n update_id?: number;\n message?: {\n text?: string;\n chat?: { id?: number | string };\n from?: { id?: number | string; is_bot?: boolean };\n };\n}\n\nexport const TELEGRAM_BACKOFF_MS = [2000, 4000, 8000, 16000, 30000] as const;\n\nexport function create_telegram_adapter(params: AdapterParams): PlatformAdapter {\n const token = read_platform_token(params.config, \"telegram\");\n if (token === undefined) {\n return create_idle_adapter(\"telegram\", `${platform_token_env(params.config, \"telegram\")} not set`);\n }\n let running = false;\n return {\n name: \"telegram\",\n start: async () => {\n running = true;\n void poll_loop(params, token, () => running);\n },\n stop: async () => {\n running = false;\n },\n };\n}\n\nasync function poll_loop(params: AdapterParams, token: string, keep_running: () => boolean): Promise<void> {\n let offset = 0;\n let backoff_index = 0;\n while (keep_running()) {\n try {\n const updates = await fetch_updates(token, offset);\n backoff_index = 0;\n for (const update of updates) {\n offset = update.update_id !== undefined ? update.update_id + 1 : offset;\n void deliver_update(params, token, update);\n }\n } catch (error) {\n logger.warn(\"gateway telegram poll failed; backing off\", error);\n await sleep(TELEGRAM_BACKOFF_MS[backoff_index] ?? 30000);\n backoff_index = Math.min(backoff_index + 1, TELEGRAM_BACKOFF_MS.length - 1);\n }\n }\n}\n\nasync function fetch_updates(token: string, offset: number): Promise<TelegramUpdate[]> {\n const url = api_url(token, \"getUpdates\") + `?timeout=50&offset=${offset}`;\n const response = await fetch(url, { signal: AbortSignal.timeout(60_000) });\n if (response.ok === false) {\n throw new Error(`getUpdates http ${response.status}`);\n }\n const body = (await response.json()) as { result?: TelegramUpdate[] };\n return Array.isArray(body.result) === true ? body.result : [];\n}\n\nasync function deliver_update(params: AdapterParams, token: string, update: TelegramUpdate): Promise<void> {\n try {\n const message = update.message;\n if (message === undefined || message.from?.is_bot === true) {\n return;\n }\n const chat_id = message.chat?.id;\n if (chat_id === undefined) {\n return;\n }\n const text = message.text ?? \"media not supported yet\";\n const reply = await run_inbound_message(\n params.handle_message,\n \"telegram\",\n String(chat_id),\n String(message.from?.id ?? \"unknown\"),\n text,\n );\n if (reply.length === 0) {\n return;\n }\n await send_reply(token, String(chat_id), reply);\n } catch (error) {\n logger.warn(\"gateway telegram deliver failed\", error);\n }\n}\n\nasync function send_reply(token: string, chat_id: string, text: string): Promise<void> {\n for (const chunk of split_text(text, TELEGRAM_MAX_MESSAGE_CHARS)) {\n await post_json(api_url(token, \"sendMessage\"), { chat_id, text: chunk });\n }\n}\n\nasync function post_json(url: string, payload: Record<string, unknown>): Promise<void> {\n const response = await fetch(url, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify(payload),\n signal: AbortSignal.timeout(30_000),\n });\n if (response.ok === false) {\n logger.warn(`gateway telegram sendMessage failed with http ${response.status}`);\n }\n}\n\nfunction api_url(token: string, method: string): string {\n return `https://api.telegram.org/bot${token}/${method}`;\n}\n\n/** Splits at the last newline/space inside each window; hard cut fallback. */\nexport function split_text(text: string, limit: number): string[] {\n const chunks: string[] = [];\n let rest = text;\n while (rest.length > limit) {\n const window = rest.slice(0, limit + 1);\n const newline = window.lastIndexOf(\"\\n\");\n const space = window.lastIndexOf(\" \");\n const cut = newline > 0 ? newline : space > 0 ? space : limit;\n chunks.push(rest.slice(0, cut));\n rest = rest.slice(cut).trimStart();\n }\n if (rest.length > 0) {\n chunks.push(rest);\n }\n return chunks;\n}","/**\n * Twitch adapter: IRC-over-WebSocket on irc-ws.chat.twitch.tv. Handles\n * CAP/JOIN setup, tag-prefixed PRIVMSG parsing, PING/PONG, and bounded\n * message splitting. Absent config degrades to an idle adapter.\n */\nimport type { AgentConfig } from \"../agent/config.js\";\nimport { logger } from \"../util/log.js\";\nimport { sleep } from \"../util/sleep.js\";\nimport { platform_token_env, read_platform_token } from \"./token_env.js\";\nimport type { AdapterParams, PlatformAdapter, RawSocket } from \"./types.js\";\nimport { create_idle_adapter, open_socket, run_inbound_message } from \"./types.js\";\n\nexport const TWITCH_IRC_URL = \"wss://irc-ws.chat.twitch.tv:443\";\n/** Content budget under Twitch's 500-char message / 512-byte IRC line caps. */\nexport const TWITCH_MESSAGE_CAP = 450;\nexport const TWITCH_BACKOFF_MS = [5000, 10000, 20000, 30000] as const;\nconst TWITCH_CHUNK_GAP_MS = 1600;\n\ninterface TwitchConfig {\n token: string;\n nick: string;\n channels: string[];\n}\n\ninterface ParsedLine {\n kind: \"ping\" | \"privmsg\" | \"welcome\" | \"other\";\n channel: string;\n user: string;\n text: string;\n}\n\nexport function create_twitch_adapter(params: AdapterParams): PlatformAdapter {\n const twitch = read_twitch_env(params.config);\n if (twitch === undefined) {\n const token_env = platform_token_env(params.config, \"twitch\");\n const reason = token_env === \"LICH_TWITCH_OAUTH_TOKEN\" ? \"LICH_TWITCH_OAUTH_TOKEN / NICK not set\" : `${token_env} / LICH_TWITCH_NICK not set`;\n return create_idle_adapter(\"twitch\", reason);\n }\n let running = false;\n let socket: RawSocket | undefined;\n let stop_controller = new AbortController();\n return {\n name: \"twitch\",\n start: async () => {\n running = true;\n stop_controller = new AbortController();\n void irc_loop(params, twitch, () => running, (opened) => {\n socket = opened;\n }, stop_controller.signal);\n },\n stop: async () => {\n running = false;\n stop_controller.abort();\n socket?.close();\n socket = undefined;\n },\n };\n}\n\nfunction read_twitch_env(config: AgentConfig): TwitchConfig | undefined {\n const raw_token = read_platform_token(config, \"twitch\");\n const nick = process.env.LICH_TWITCH_NICK;\n const channels_env = process.env.LICH_TWITCH_CHANNELS;\n if (raw_token === undefined || raw_token.length === 0 || nick === undefined || nick.length === 0) {\n return undefined;\n }\n const token = raw_token.startsWith(\"oauth:\") === true ? raw_token : `oauth:${raw_token}`;\n const channels = (channels_env ?? \"\")\n .split(\",\")\n .map((channel) => channel.trim().toLowerCase())\n .filter((channel) => channel.length > 0);\n if (channels.length === 0) {\n return undefined;\n }\n return { token, nick, channels };\n}\n\nasync function irc_loop(\n params: AdapterParams,\n twitch: TwitchConfig,\n keep_running: () => boolean,\n set_socket: (socket: RawSocket) => void,\n signal: AbortSignal,\n): Promise<void> {\n let backoff_index = 0;\n while (keep_running()) {\n let close_code: number | undefined;\n let welcomed = false;\n try {\n const socket = await open_socket(TWITCH_IRC_URL);\n set_socket(socket);\n const session = await irc_session(params, twitch, socket);\n close_code = session.close_code;\n welcomed = session.welcomed;\n } catch (error) {\n logger.warn(\"gateway twitch connection failed\", error);\n }\n if (keep_running() === false) {\n return;\n }\n if (welcomed === true) {\n backoff_index = 0;\n }\n const delay = TWITCH_BACKOFF_MS[backoff_index] ?? 30000;\n const close_note = close_code === undefined ? \"\" : ` (close ${close_code})`;\n logger.warn(`gateway twitch reconnecting in ${delay}ms${close_note}`);\n try {\n await sleep(delay, signal);\n } catch {\n return;\n }\n backoff_index = Math.min(backoff_index + 1, TWITCH_BACKOFF_MS.length - 1);\n }\n}\n\nasync function irc_session(\n params: AdapterParams,\n twitch: TwitchConfig,\n socket: RawSocket,\n): Promise<{ close_code: number; welcomed: boolean }> {\n let welcomed = false;\n const closed = new Promise<number>((resolve) => {\n socket.onclose = ((event?: { code?: number }) => {\n resolve(typeof event?.code === \"number\" ? event.code : 1000);\n }) as () => void;\n });\n socket.onmessage = (event) => {\n for (const line of String(event.data).split(\"\\r\\n\")) {\n void handle_irc_line(params, twitch, socket, line, (ok) => {\n if (ok === true) {\n welcomed = true;\n }\n });\n }\n };\n socket.send(\"CAP REQ :twitch.tv/tags twitch.tv/commands\");\n socket.send(`PASS ${twitch.token}`);\n socket.send(`NICK ${twitch.nick}`);\n for (const channel of twitch.channels) {\n socket.send(`JOIN #${channel}`);\n }\n const close_code = await closed;\n return { close_code, welcomed };\n}\n\nasync function handle_irc_line(\n params: AdapterParams,\n twitch: TwitchConfig,\n socket: RawSocket,\n line: string,\n on_welcome: (ok: boolean) => void,\n): Promise<void> {\n if (line.length === 0) {\n return;\n }\n const parsed = parse_irc_line(line);\n if (parsed.kind === \"welcome\") {\n on_welcome(true);\n return;\n }\n if (parsed.kind === \"ping\") {\n socket.send(\"PONG :tmi.twitch.tv\");\n return;\n }\n if (parsed.kind !== \"privmsg\" || parsed.user === twitch.nick) {\n return;\n }\n const reply = await run_inbound_message(\n params.handle_message,\n \"twitch\",\n parsed.channel,\n parsed.user,\n parsed.text,\n );\n await send_twitch_message(socket, parsed.channel, reply);\n}\n\nasync function send_twitch_message(socket: RawSocket, channel: string, text: string): Promise<void> {\n const safe = sanitize_twitch_outbound(text);\n if (safe.length === 0) {\n return;\n }\n const chunks = split_chunks(safe, TWITCH_MESSAGE_CAP);\n for (let index = 0; index < chunks.length; index += 1) {\n const chunk = chunks[index];\n if (chunk === undefined) {\n continue;\n }\n const sanitized_chunk = sanitize_twitch_outbound(chunk);\n if (sanitized_chunk.length === 0) {\n continue;\n }\n socket.send(`PRIVMSG #${channel} :${sanitized_chunk}`);\n if (index + 1 < chunks.length) {\n await sleep(TWITCH_CHUNK_GAP_MS);\n }\n }\n}\n\n/** Strip IRC control chars and neutralize leading Twitch chat command markers. */\nexport function sanitize_twitch_outbound(text: string): string {\n let cleaned = text.replace(/[\\r\\n]+/g, \" \").trim();\n while (cleaned.startsWith(\"/\") === true || cleaned.startsWith(\".\") === true) {\n cleaned = cleaned.slice(1).trimStart();\n }\n return cleaned;\n}\n\nexport function parse_irc_line(line: string): ParsedLine {\n if (line.startsWith(\"PING\") === true) {\n return { kind: \"ping\", channel: \"\", user: \"\", text: \"\" };\n }\n if (/:\\S+ 001 /.test(line) === true || line.includes(\" GLOBALUSERSTATE \") === true) {\n return { kind: \"welcome\", channel: \"\", user: \"\", text: \"\" };\n }\n const privmsg = match_privmsg(line);\n if (privmsg === undefined) {\n return { kind: \"other\", channel: \"\", user: \"\", text: \"\" };\n }\n return privmsg;\n}\n\nfunction match_privmsg(line: string): ParsedLine | undefined {\n const without_tags = line.startsWith(\"@\") === true ? line.slice(line.indexOf(\" \") + 1) : line;\n const matched = /^:(\\w+)!\\S+ PRIVMSG #(\\w+) :(.*)$/.exec(without_tags);\n if (matched === null) {\n return undefined;\n }\n return {\n kind: \"privmsg\",\n user: matched[1] ?? \"\",\n channel: matched[2] ?? \"\",\n text: matched[3] ?? \"\",\n };\n}\n\nfunction split_chunks(text: string, limit: number): string[] {\n const chunks: string[] = [];\n let rest = text;\n while (rest.length > limit) {\n chunks.push(rest.slice(0, limit));\n rest = rest.slice(limit);\n }\n if (rest.length > 0) {\n chunks.push(rest);\n }\n return chunks;\n}\n","/**\n * Webhook adapter: the default zero-config platform. A plain node:http\n * server exposing POST /message (+ GET /health), optionally guarded by a\n * shared token via the x-lich-token header. Binds loopback by default.\n */\nimport { createServer, type IncomingMessage, type Server, type ServerResponse } from \"node:http\";\nimport { logger } from \"../util/log.js\";\nimport { format_agent_reply } from \"./format.js\";\nimport { read_platform_token } from \"./token_env.js\";\nimport type { AdapterParams, PlatformAdapter } from \"./types.js\";\n\nexport const DEFAULT_GATEWAY_PORT = 8089;\nexport const DEFAULT_GATEWAY_HOST = \"127.0.0.1\";\n/** Reject POST bodies larger than this (G-6). */\nexport const MAX_WEBHOOK_BODY_BYTES = 1_000_000;\n\ninterface WebhookAdapterParams extends AdapterParams {\n port?: number;\n host?: string;\n /** Test hook: reports the resolved bound port once listening. */\n on_listening?: (port: number) => void;\n}\n\ninterface IncomingPayload {\n chat_id?: unknown;\n user_id?: unknown;\n text?: unknown;\n}\n\nexport function create_webhook_adapter(params: WebhookAdapterParams): PlatformAdapter {\n const port = params.port ?? read_port_env() ?? DEFAULT_GATEWAY_PORT;\n const host = params.host ?? read_host_env() ?? DEFAULT_GATEWAY_HOST;\n const token = read_platform_token(params.config, \"webhook\");\n let server: Server | undefined;\n\n return {\n name: \"webhook\",\n start: async () => {\n assert_bind_allowed(host, token);\n server = createServer((request, response) => {\n void dispatch_webhook(params, request, response, token);\n });\n await listen_on(server, port, host, (bound) => {\n params.on_listening?.(bound);\n });\n },\n stop: async () => {\n await close_server(server);\n server = undefined;\n },\n };\n}\n\nasync function dispatch_webhook(\n params: AdapterParams,\n request: IncomingMessage,\n response: ServerResponse,\n token: string | undefined,\n): Promise<void> {\n try {\n if (request.method === \"GET\" && request.url === \"/health\") {\n send_json(response, 200, { status: \"ok\" });\n return;\n }\n if (request.method !== \"POST\" || request.url !== \"/message\") {\n send_json(response, 404, { error: \"not found\" });\n return;\n }\n if (token !== undefined && request.headers[\"x-lich-token\"] !== token) {\n send_json(response, 401, { error: \"unauthorized\" });\n return;\n }\n await handle_message_post(params, request, response);\n } catch (error) {\n logger.error(\"gateway webhook request failed\", error);\n if (response.headersSent === false) {\n send_json(response, 500, { error: \"internal error\" });\n }\n }\n}\n\nasync function handle_message_post(\n params: AdapterParams,\n request: IncomingMessage,\n response: ServerResponse,\n): Promise<void> {\n if (content_length_exceeds(request, MAX_WEBHOOK_BODY_BYTES) === true) {\n request.resume();\n send_json(response, 413, { error: \"payload too large\" });\n return;\n }\n const body = await read_body(request, MAX_WEBHOOK_BODY_BYTES);\n if (body === undefined) {\n send_json(response, 413, { error: \"payload too large\" });\n return;\n }\n const payload = parse_payload(body);\n const text = payload.text;\n if (text === undefined) {\n send_json(response, 400, { error: \"text is required\" });\n return;\n }\n // G-5: callers must not choose another platform's conversation key.\n const platform = \"webhook\";\n const chat_id = payload.chat_id ?? \"default\";\n const user_id = payload.user_id ?? \"anonymous\";\n const reply = await params.handle_message(platform, String(chat_id), String(user_id), String(text));\n respond_json_text(response, 200, format_agent_reply(reply ?? \"\", undefined, \"webhook\"));\n}\n\nfunction content_length_exceeds(request: IncomingMessage, max_bytes: number): boolean {\n const raw = request.headers[\"content-length\"];\n if (raw === undefined) {\n return false;\n }\n const length = Number(raw);\n return Number.isFinite(length) === true && length > max_bytes;\n}\n\n/** Reads the request body; returns undefined when the stream exceeds max_bytes. */\nfunction read_body(request: IncomingMessage, max_bytes: number): Promise<string | undefined> {\n return new Promise((resolve, reject) => {\n let body = \"\";\n let size = 0;\n let too_large = false;\n request.on(\"data\", (chunk: Buffer) => {\n if (too_large === true) {\n return;\n }\n size += chunk.length;\n if (size > max_bytes) {\n too_large = true;\n body = \"\";\n return;\n }\n body += chunk.toString(\"utf8\");\n });\n request.on(\"end\", () => resolve(too_large === true ? undefined : body));\n request.on(\"error\", reject);\n });\n}\n\nfunction parse_payload(body: string): IncomingPayload {\n if (body.trim().length === 0) {\n return {};\n }\n try {\n const parsed = JSON.parse(body) as unknown;\n if (typeof parsed !== \"object\" || parsed === null) {\n return {};\n }\n return parsed as IncomingPayload;\n } catch {\n return {};\n }\n}\n\nfunction send_json(response: ServerResponse, status: number, payload: Record<string, unknown>): void {\n respond_json_text(response, status, JSON.stringify(payload));\n}\n\nfunction respond_json_text(response: ServerResponse, status: number, json_text: string): void {\n response.statusCode = status;\n response.setHeader(\"content-type\", \"application/json\");\n response.end(json_text);\n}\n\nfunction listen_on(\n server: Server,\n port: number,\n host: string,\n on_listening: (port: number) => void,\n): Promise<void> {\n return new Promise((resolve, reject) => {\n server.once(\"error\", reject);\n server.listen(port, host, () => {\n const bound = (server.address() as { port?: number } | null)?.port ?? port;\n logger.info(`gateway webhook listening on ${host}:${bound}`);\n on_listening(bound);\n resolve();\n });\n });\n}\n\nasync function close_server(server: Server | undefined): Promise<void> {\n if (server === undefined) {\n return;\n }\n await new Promise<void>((resolve) => {\n server.close(() => resolve());\n });\n}\n\n/** Loopback binds are always fine; non-loopback requires an auth token. */\nexport function assert_bind_allowed(host: string, token: string | undefined): void {\n if (is_loopback_host(host) === true) {\n return;\n }\n if (token === undefined || token.length === 0) {\n throw new Error(\n `gateway webhook refuses non-loopback bind (${host}) without a token — set LICH_GATEWAY_TOKEN or bind 127.0.0.1`,\n );\n }\n logger.warn(\n `gateway webhook binding ${host} with token auth — this exposes the agent beyond loopback; keep the token secret`,\n );\n}\n\nexport function is_loopback_host(host: string): boolean {\n const normalized = host.trim().toLowerCase();\n return normalized === \"127.0.0.1\" || normalized === \"::1\" || normalized === \"localhost\";\n}\n\nfunction read_port_env(): number | undefined {\n const raw = process.env.LICH_GATEWAY_PORT;\n if (raw === undefined || raw.length === 0) {\n return undefined;\n }\n const port = Number(raw);\n return Number.isInteger(port) && port > 0 && port < 65536 ? port : undefined;\n}\n\nfunction read_host_env(): string | undefined {\n const raw = process.env.LICH_GATEWAY_HOST;\n if (raw === undefined || raw.trim().length === 0) {\n return undefined;\n }\n return raw.trim();\n}\n","/**\n * Gateway reply formatting: per-platform shaping and whitespace-aware\n * splitting for size-capped transports (telegram 4096, discord 2000, …).\n */\nimport type { Usage } from \"../providers/types.js\";\n\nconst USAGE_FOOTER_PREFIX = \"\\n\\n_tokens: \";\n\nexport function format_agent_reply(text: string, usage: Usage | undefined, platform: string): string {\n if (platform === \"webhook\") {\n return JSON.stringify({ reply: text, usage: usage ?? null });\n }\n const total_tokens = usage?.total_tokens ?? 0;\n if (total_tokens > 0) {\n return `${text}${USAGE_FOOTER_PREFIX}${total_tokens}_`;\n }\n return text;\n}\n\n/** Splits text into chunks of at most `limit` chars, preferring whitespace. */\nexport function split_text(text: string, limit: number): string[] {\n const chunks: string[] = [];\n let rest = text;\n while (rest.length > limit) {\n const cut = find_split_point(rest, limit);\n chunks.push(rest.slice(0, cut));\n rest = rest.slice(cut).trimStart();\n }\n if (rest.length > 0) {\n chunks.push(rest);\n }\n return chunks;\n}\n\n/** Last newline or space inside the window; falls back to a hard cut. */\nfunction find_split_point(text: string, limit: number): number {\n const window = text.slice(0, limit + 1);\n const newline = window.lastIndexOf(\"\\n\");\n if (newline > 0) {\n return newline;\n }\n const space = window.lastIndexOf(\" \");\n if (space > 0) {\n return space;\n }\n return limit;\n}","/**\n * Gateway runner: wires the shared Agent, the conversation bus, and the\n * requested platform adapters, then stays alive until a signal arrives.\n */\nimport { create_agent_with_plugins, type Agent } from \"../agent/agent.js\";\nimport type { AgentConfig } from \"../agent/config.js\";\nimport { logger } from \"../util/log.js\";\nimport { gateway_tools_enabled } from \"./access.js\";\nimport { GatewayBus } from \"./bus.js\";\nimport { create_discord_adapter } from \"./discord.js\";\nimport { create_telegram_adapter } from \"./telegram.js\";\nimport { create_twitch_adapter } from \"./twitch.js\";\nimport type { AdapterParams, PlatformAdapter } from \"./types.js\";\nimport { create_webhook_adapter } from \"./webhook.js\";\n\n/** Preload plugins, then hand that same agent to the bus factory. */\nexport async function create_gateway_bus(config: AgentConfig): Promise<{ agent: Agent; bus: GatewayBus }> {\n const agent_config = { ...config, tools_enabled: gateway_tools_enabled(config) };\n const agent = await create_agent_with_plugins(agent_config);\n const bus = new GatewayBus({ config, agent_factory: () => agent, wire_tool_logging: true });\n return { agent, bus };\n}\n\nexport async function run_gateway(config: AgentConfig, platforms: readonly string[]): Promise<number> {\n const valid: string[] = [];\n for (const platform of platforms) {\n if (is_known_platform(platform) === true) {\n valid.push(platform);\n } else {\n process.stderr.write(`lich: unknown gateway platform \"${platform}\" (skipping)\\n`);\n }\n }\n if (valid.length === 0) {\n process.stderr.write(\"lich: gateway needs at least one valid platform (webhook|telegram|discord|twitch)\\n\");\n return 1;\n }\n const { agent, bus } = await create_gateway_bus(config);\n const adapters = build_adapters(config, bus, valid);\n install_signal_handlers(agent, bus, adapters);\n logger.info(`gateway starting: platforms=${valid.join(\",\")}, port=${process.env.LICH_GATEWAY_PORT ?? \"8089\"}, pid=${process.pid}`);\n await start_all_adapters(adapters);\n return await new Promise<number>(() => undefined);\n}\n\nfunction is_known_platform(platform: string): boolean {\n return platform === \"webhook\" || platform === \"telegram\" || platform === \"discord\" || platform === \"twitch\";\n}\n\nfunction build_adapters(config: AgentConfig, bus: GatewayBus, platforms: readonly string[]): PlatformAdapter[] {\n const params: AdapterParams = {\n config,\n handle_message: (platform, chat_id, user_id, text) => bus.handle(platform, chat_id, user_id, text),\n get_agent: () => {\n throw new Error(\"get_agent is reserved for future use\");\n },\n reply_router: () => undefined,\n };\n const adapters: PlatformAdapter[] = [];\n for (const platform of platforms) {\n const adapter = create_platform_adapter(params, platform);\n if (adapter !== undefined) {\n adapters.push(adapter);\n }\n }\n return adapters;\n}\n\nfunction create_platform_adapter(params: AdapterParams, platform: string): PlatformAdapter | undefined {\n switch (platform) {\n case \"webhook\":\n return create_webhook_adapter({ ...params, port: read_webhook_port(), host: read_webhook_host() });\n case \"telegram\":\n return create_telegram_adapter(params);\n case \"discord\":\n return create_discord_adapter(params);\n case \"twitch\":\n return create_twitch_adapter(params);\n default:\n return undefined;\n }\n}\n\nfunction read_webhook_port(): number | undefined {\n const raw = process.env.LICH_GATEWAY_PORT;\n if (raw === undefined || raw.length === 0) {\n return undefined;\n }\n const port = Number(raw);\n return Number.isInteger(port) && port > 0 && port < 65536 ? port : undefined;\n}\n\nfunction read_webhook_host(): string | undefined {\n const raw = process.env.LICH_GATEWAY_HOST;\n if (raw === undefined || raw.trim().length === 0) {\n return undefined;\n }\n return raw.trim();\n}\n\n/** Adapter start failures log and are skipped; the rest keep running. */\nasync function start_all_adapters(adapters: readonly PlatformAdapter[]): Promise<void> {\n for (const adapter of adapters) {\n try {\n await adapter.start();\n logger.info(`gateway adapter started: ${adapter.name}`);\n } catch (error) {\n logger.error(`gateway adapter failed to start: ${adapter.name}`, error);\n }\n }\n}\n\nfunction install_signal_handlers(agent: Agent, bus: GatewayBus, adapters: readonly PlatformAdapter[]): void {\n const shutdown = (): void => {\n for (const adapter of adapters) {\n void adapter\n .stop()\n .catch((error: unknown) => logger.warn(`gateway adapter stop failed: ${adapter.name}`, error));\n }\n bus.stop();\n agent.close();\n process.exit(0);\n };\n process.once(\"SIGINT\", shutdown);\n process.once(\"SIGTERM\", shutdown);\n}"],"mappings":"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","names":["split_chunks","parse_payload"]}
|