@friendlyrobot/discord-pi-agent 0.6.1 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +32 -0
- package/dist/index.js +131 -84
- package/dist/logger.d.ts +3 -0
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -96,6 +96,38 @@ The initial post body becomes the first prompt. Sessions survive restarts.
|
|
|
96
96
|
- `startupMessage` default: `Bot is online and ready.`
|
|
97
97
|
- `shutdownOnSignals` default: `true`
|
|
98
98
|
|
|
99
|
+
### Logging
|
|
100
|
+
|
|
101
|
+
The package uses `pino` for structured logs.
|
|
102
|
+
|
|
103
|
+
Behavior:
|
|
104
|
+
|
|
105
|
+
- when stdout is a TTY, logs use `pino-pretty` for readable local console output
|
|
106
|
+
- when stdout is not a TTY, logs stay as JSON
|
|
107
|
+
|
|
108
|
+
Log level env vars:
|
|
109
|
+
|
|
110
|
+
- `DISCORD_PI_AGENT_LOG_LEVEL`
|
|
111
|
+
- `LOG_LEVEL` fallback
|
|
112
|
+
|
|
113
|
+
Default level is `info`.
|
|
114
|
+
|
|
115
|
+
For detailed prompt and tool monitoring during local runs, use:
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
DISCORD_PI_AGENT_LOG_LEVEL=debug
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Pretty console logs use:
|
|
122
|
+
|
|
123
|
+
- colors
|
|
124
|
+
- local timestamp (`SYS:standard`)
|
|
125
|
+
- level first
|
|
126
|
+
- hidden `pid` and `hostname`
|
|
127
|
+
- module-aware labels like `[discord-gateway]`
|
|
128
|
+
- direction markers like `IN` and `OUT`
|
|
129
|
+
- multi-line payload blocks for easier input/output inspection
|
|
130
|
+
|
|
99
131
|
### Forum channel options
|
|
100
132
|
|
|
101
133
|
- `discordAllowedForumChannelIds` — string array of forum channel IDs to respond in
|
package/dist/index.js
CHANGED
|
@@ -13,8 +13,35 @@ import {
|
|
|
13
13
|
SettingsManager
|
|
14
14
|
} from "@mariozechner/pi-coding-agent";
|
|
15
15
|
|
|
16
|
+
// src/logger.ts
|
|
17
|
+
import pino from "pino";
|
|
18
|
+
var loggerLevel = process.env.DISCORD_PI_AGENT_LOG_LEVEL || process.env.LOG_LEVEL || "info";
|
|
19
|
+
var usePrettyTransport = process.stdout.isTTY;
|
|
20
|
+
var baseOptions = {
|
|
21
|
+
level: loggerLevel
|
|
22
|
+
};
|
|
23
|
+
var logger = usePrettyTransport ? pino({
|
|
24
|
+
...baseOptions,
|
|
25
|
+
transport: {
|
|
26
|
+
target: "pino-pretty",
|
|
27
|
+
options: {
|
|
28
|
+
colorize: true,
|
|
29
|
+
colorizeObjects: true,
|
|
30
|
+
levelFirst: true,
|
|
31
|
+
translateTime: "SYS:standard",
|
|
32
|
+
ignore: "pid,hostname",
|
|
33
|
+
singleLine: false,
|
|
34
|
+
messageFormat: "[{module}] {if direction}{direction} {end}{msg}"
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}) : pino(baseOptions);
|
|
38
|
+
function createModuleLogger(moduleName) {
|
|
39
|
+
return logger.child({ module: moduleName });
|
|
40
|
+
}
|
|
41
|
+
|
|
16
42
|
// src/markdown-table-transformer.ts
|
|
17
43
|
import { Lexer } from "marked";
|
|
44
|
+
var logger2 = createModuleLogger("markdown-table-transformer");
|
|
18
45
|
var CODE_BLOCK_WRAPPER = "```\n{TABLE}\n```";
|
|
19
46
|
async function transformMarkdownTablesToCodeBlocks(text) {
|
|
20
47
|
const normalized = normalizeCodeFences(text);
|
|
@@ -58,19 +85,25 @@ async function formatWithPrettier(text) {
|
|
|
58
85
|
});
|
|
59
86
|
return formatted.trim();
|
|
60
87
|
} catch (error) {
|
|
61
|
-
|
|
88
|
+
logger2.error({
|
|
89
|
+
error
|
|
90
|
+
}, "Prettier formatting failed");
|
|
62
91
|
return text;
|
|
63
92
|
}
|
|
64
93
|
}
|
|
65
94
|
|
|
66
95
|
// src/reply-buffer.ts
|
|
96
|
+
var logger3 = createModuleLogger("reply-buffer");
|
|
67
97
|
async function collectReply(session, prompt, options = {}) {
|
|
68
98
|
const logPrefix = options.logPrefix ?? "[agent]";
|
|
69
99
|
let streamedText = "";
|
|
70
100
|
let eventCount = 0;
|
|
71
101
|
let toolCount = 0;
|
|
72
102
|
let sawAgentEnd = false;
|
|
73
|
-
|
|
103
|
+
logger3.debug({
|
|
104
|
+
logPrefix,
|
|
105
|
+
promptLength: prompt.length
|
|
106
|
+
}, "prompt start");
|
|
74
107
|
const unsubscribe = session.subscribe((event) => {
|
|
75
108
|
eventCount += 1;
|
|
76
109
|
if (event.type === "message_update") {
|
|
@@ -81,23 +114,26 @@ async function collectReply(session, prompt, options = {}) {
|
|
|
81
114
|
}
|
|
82
115
|
if (event.type === "tool_execution_start") {
|
|
83
116
|
toolCount += 1;
|
|
84
|
-
|
|
117
|
+
logger3.debug({
|
|
85
118
|
toolName: event.toolName,
|
|
86
|
-
input: truncateForLog(JSON.stringify(event.args))
|
|
87
|
-
|
|
119
|
+
input: truncateForLog(JSON.stringify(event.args)),
|
|
120
|
+
logPrefix
|
|
121
|
+
}, "tool start");
|
|
88
122
|
}
|
|
89
123
|
if (event.type === "tool_execution_end") {
|
|
90
|
-
|
|
124
|
+
logger3.debug({
|
|
91
125
|
toolName: event.toolName,
|
|
92
126
|
isError: event.isError,
|
|
93
|
-
output: truncateForLog(extractToolOutput(event.result))
|
|
94
|
-
|
|
127
|
+
output: truncateForLog(extractToolOutput(event.result)),
|
|
128
|
+
logPrefix
|
|
129
|
+
}, "tool end");
|
|
95
130
|
}
|
|
96
131
|
if (event.type === "agent_end") {
|
|
97
132
|
sawAgentEnd = true;
|
|
98
|
-
|
|
99
|
-
messageCount: event.messages.length
|
|
100
|
-
|
|
133
|
+
logger3.debug({
|
|
134
|
+
messageCount: event.messages.length,
|
|
135
|
+
logPrefix
|
|
136
|
+
}, "agent end");
|
|
101
137
|
}
|
|
102
138
|
});
|
|
103
139
|
try {
|
|
@@ -108,25 +144,26 @@ async function collectReply(session, prompt, options = {}) {
|
|
|
108
144
|
const errorMessage = session.agent.state.errorMessage?.trim();
|
|
109
145
|
const fallbackText = getLatestAssistantText(session.messages);
|
|
110
146
|
const finalText = streamedText.trim() || fallbackText.trim();
|
|
111
|
-
|
|
147
|
+
logger3.debug({
|
|
112
148
|
eventCount,
|
|
113
149
|
toolCount,
|
|
114
150
|
sawAgentEnd,
|
|
115
151
|
streamedTextLength: streamedText.trim().length,
|
|
116
152
|
fallbackTextLength: fallbackText.trim().length,
|
|
117
|
-
errorMessage
|
|
118
|
-
|
|
153
|
+
errorMessage,
|
|
154
|
+
logPrefix
|
|
155
|
+
}, "prompt done");
|
|
119
156
|
if (errorMessage) {
|
|
120
157
|
return errorMessage;
|
|
121
158
|
}
|
|
122
159
|
if (finalText) {
|
|
123
160
|
const transformed = await transformMarkdownTablesToCodeBlocks(finalText);
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
161
|
+
logger3.debug({
|
|
162
|
+
logPrefix,
|
|
163
|
+
finalTextLength: finalText.length,
|
|
164
|
+
transformedTextLength: transformed.length,
|
|
165
|
+
transformed: transformed !== finalText
|
|
166
|
+
}, "assistant text ready");
|
|
130
167
|
return transformed;
|
|
131
168
|
}
|
|
132
169
|
return "No response generated.";
|
|
@@ -163,6 +200,8 @@ function getLatestAssistantText(messages) {
|
|
|
163
200
|
}
|
|
164
201
|
|
|
165
202
|
// src/agent-service.ts
|
|
203
|
+
var logger4 = createModuleLogger("agent-service");
|
|
204
|
+
|
|
166
205
|
class AgentService {
|
|
167
206
|
config;
|
|
168
207
|
authStorage;
|
|
@@ -184,19 +223,19 @@ class AgentService {
|
|
|
184
223
|
async initialize() {
|
|
185
224
|
await fs.mkdir(this.config.agentDir, { recursive: true });
|
|
186
225
|
await fs.mkdir(this.getSessionDir(), { recursive: true });
|
|
187
|
-
|
|
226
|
+
logger4.info({
|
|
188
227
|
cwd: this.config.cwd,
|
|
189
228
|
agentDir: this.config.agentDir,
|
|
190
229
|
sessionDir: this.getSessionDir(),
|
|
191
230
|
modelProvider: this.config.modelProvider,
|
|
192
231
|
modelId: this.config.modelId,
|
|
193
232
|
thinkingLevel: this.config.thinkingLevel
|
|
194
|
-
});
|
|
233
|
+
}, "config");
|
|
195
234
|
await this.resourceLoader.reload();
|
|
196
|
-
|
|
235
|
+
logger4.info({
|
|
197
236
|
extensions: this.resourceLoader.getExtensions().extensions.map((extension) => extension.path),
|
|
198
237
|
agentsFiles: this.resourceLoader.getAgentsFiles().agentsFiles.map((file) => file.path)
|
|
199
|
-
});
|
|
238
|
+
}, "resources loaded");
|
|
200
239
|
await this.createOrResumeSession();
|
|
201
240
|
await this.ensureConfiguredModel();
|
|
202
241
|
}
|
|
@@ -218,11 +257,11 @@ class AgentService {
|
|
|
218
257
|
sessionManager: SessionManager.continueRecent(this.config.cwd, sessionDir),
|
|
219
258
|
thinkingLevel: this.config.thinkingLevel
|
|
220
259
|
});
|
|
221
|
-
|
|
260
|
+
logger4.debug({
|
|
222
261
|
sessionDir,
|
|
223
262
|
sessionId: session.sessionId,
|
|
224
263
|
sessionFile: session.sessionFile
|
|
225
|
-
});
|
|
264
|
+
}, "scoped session created");
|
|
226
265
|
await this.ensureModelForSession(session);
|
|
227
266
|
return session;
|
|
228
267
|
}
|
|
@@ -302,11 +341,11 @@ class AgentService {
|
|
|
302
341
|
thinkingLevel: this.config.thinkingLevel
|
|
303
342
|
});
|
|
304
343
|
this.session = session;
|
|
305
|
-
|
|
344
|
+
logger4.info({
|
|
306
345
|
sessionId: session.sessionId,
|
|
307
346
|
sessionFile: session.sessionFile,
|
|
308
347
|
restoredModel: session.model ? `${session.model.provider}/${session.model.id}` : null
|
|
309
|
-
});
|
|
348
|
+
}, "session ready");
|
|
310
349
|
}
|
|
311
350
|
async ensureConfiguredModel() {
|
|
312
351
|
await this.ensureModelForSession(this.requireSession());
|
|
@@ -314,25 +353,25 @@ class AgentService {
|
|
|
314
353
|
async ensureModelForSession(session) {
|
|
315
354
|
const desiredModel = this.modelRegistry.find(this.config.modelProvider, this.config.modelId);
|
|
316
355
|
const availableModels = await this.modelRegistry.getAvailable();
|
|
317
|
-
|
|
356
|
+
logger4.debug({
|
|
318
357
|
count: availableModels.length,
|
|
319
358
|
matches: availableModels.filter((model) => {
|
|
320
359
|
return model.provider === this.config.modelProvider;
|
|
321
360
|
}).map((model) => `${model.provider}/${model.id}`)
|
|
322
|
-
});
|
|
361
|
+
}, "available models");
|
|
323
362
|
if (!desiredModel) {
|
|
324
363
|
throw new Error(`Configured model not found: ${this.config.modelProvider}/${this.config.modelId}. Check your pi agent config and installed extensions.`);
|
|
325
364
|
}
|
|
326
365
|
if (isSameModel(session.model, desiredModel)) {
|
|
327
|
-
|
|
366
|
+
logger4.debug({
|
|
328
367
|
model: `${desiredModel.provider}/${desiredModel.id}`
|
|
329
|
-
});
|
|
368
|
+
}, "model already selected");
|
|
330
369
|
return;
|
|
331
370
|
}
|
|
332
|
-
|
|
371
|
+
logger4.info({
|
|
333
372
|
from: session.model ? `${session.model.provider}/${session.model.id}` : null,
|
|
334
373
|
to: `${desiredModel.provider}/${desiredModel.id}`
|
|
335
|
-
});
|
|
374
|
+
}, "switching model");
|
|
336
375
|
await session.setModel(desiredModel);
|
|
337
376
|
await this.applyConfiguredThinkingLevelForSession(session);
|
|
338
377
|
}
|
|
@@ -350,14 +389,14 @@ class AgentService {
|
|
|
350
389
|
const available = session.getAvailableThinkingLevels();
|
|
351
390
|
if (available.includes(this.config.thinkingLevel)) {
|
|
352
391
|
session.setThinkingLevel(this.config.thinkingLevel);
|
|
353
|
-
|
|
392
|
+
logger4.debug({
|
|
354
393
|
level: this.config.thinkingLevel
|
|
355
|
-
});
|
|
394
|
+
}, "thinking level applied");
|
|
356
395
|
} else {
|
|
357
|
-
|
|
396
|
+
logger4.debug({
|
|
358
397
|
requested: this.config.thinkingLevel,
|
|
359
398
|
available
|
|
360
|
-
});
|
|
399
|
+
}, "thinking level not available for model");
|
|
361
400
|
}
|
|
362
401
|
}
|
|
363
402
|
}
|
|
@@ -855,6 +894,7 @@ function normalizeContextValue(value) {
|
|
|
855
894
|
}
|
|
856
895
|
|
|
857
896
|
// src/discord-gateway-client.ts
|
|
897
|
+
var logger5 = createModuleLogger("discord-gateway");
|
|
858
898
|
function getAuthorDisplayName(message) {
|
|
859
899
|
return message.member?.displayName || message.author.globalName || message.author.username;
|
|
860
900
|
}
|
|
@@ -916,17 +956,18 @@ function stopTypingInterval(interval) {
|
|
|
916
956
|
async function sendReply(message, text) {
|
|
917
957
|
const channel = message.channel;
|
|
918
958
|
if (!channel.isSendable()) {
|
|
919
|
-
|
|
959
|
+
logger5.debug({
|
|
920
960
|
messageId: message.id
|
|
921
|
-
});
|
|
961
|
+
}, "reply skipped, channel not sendable");
|
|
922
962
|
return;
|
|
923
963
|
}
|
|
924
964
|
const chunks = chunkMessage(text);
|
|
925
|
-
|
|
965
|
+
logger5.debug({
|
|
966
|
+
direction: "OUT",
|
|
926
967
|
messageId: message.id,
|
|
927
968
|
chunkCount: chunks.length,
|
|
928
969
|
textLength: text.length
|
|
929
|
-
});
|
|
970
|
+
}, "sending reply");
|
|
930
971
|
const [firstChunk, ...remainingChunks] = chunks;
|
|
931
972
|
if (!firstChunk) {
|
|
932
973
|
return;
|
|
@@ -937,10 +978,10 @@ async function sendReply(message, text) {
|
|
|
937
978
|
await channel.send(chunk);
|
|
938
979
|
}
|
|
939
980
|
} catch (error) {
|
|
940
|
-
|
|
981
|
+
logger5.error({
|
|
941
982
|
messageId: message.id,
|
|
942
983
|
error
|
|
943
|
-
});
|
|
984
|
+
}, "send reply failed");
|
|
944
985
|
}
|
|
945
986
|
}
|
|
946
987
|
async function startGatewayClient(config, agentService, sessionRegistry, authConfig) {
|
|
@@ -954,7 +995,7 @@ async function startGatewayClient(config, agentService, sessionRegistry, authCon
|
|
|
954
995
|
partials: [Partials.Channel]
|
|
955
996
|
});
|
|
956
997
|
client.once(Events.ClientReady, async (readyClient) => {
|
|
957
|
-
|
|
998
|
+
logger5.info({ userTag: readyClient.user.tag }, "logged in");
|
|
958
999
|
if (!authConfig.startupMessage) {
|
|
959
1000
|
return;
|
|
960
1001
|
}
|
|
@@ -962,30 +1003,31 @@ async function startGatewayClient(config, agentService, sessionRegistry, authCon
|
|
|
962
1003
|
const user = await readyClient.users.fetch(authConfig.discordAllowedUserId);
|
|
963
1004
|
const dmChannel = await user.createDM();
|
|
964
1005
|
await dmChannel.send(authConfig.startupMessage);
|
|
965
|
-
|
|
1006
|
+
logger5.info({
|
|
966
1007
|
userId: authConfig.discordAllowedUserId
|
|
967
|
-
});
|
|
1008
|
+
}, "sent startup dm");
|
|
968
1009
|
} catch (error) {
|
|
969
|
-
|
|
1010
|
+
logger5.error({ error }, "failed to send startup dm");
|
|
970
1011
|
}
|
|
971
1012
|
});
|
|
972
1013
|
client.on(Events.MessageCreate, async (message) => {
|
|
973
|
-
|
|
1014
|
+
logger5.info({
|
|
1015
|
+
direction: "IN",
|
|
974
1016
|
messageId: message.id,
|
|
975
1017
|
authorId: message.author.id,
|
|
976
1018
|
channelType: message.channel.type,
|
|
977
|
-
content: message.content
|
|
978
|
-
});
|
|
1019
|
+
content: message.content
|
|
1020
|
+
}, "message received");
|
|
979
1021
|
try {
|
|
980
1022
|
await onMessage(message, config, agentService, sessionRegistry, authConfig);
|
|
981
1023
|
} catch (error) {
|
|
982
|
-
|
|
1024
|
+
logger5.error({ error, direction: "IN" }, "message handling failed");
|
|
983
1025
|
await sendReply(message, "The bot hit an error while handling that message.");
|
|
984
1026
|
}
|
|
985
1027
|
});
|
|
986
1028
|
client.on(Events.ThreadDelete, async (thread) => {
|
|
987
1029
|
const scope = `thread:${thread.id}`;
|
|
988
|
-
|
|
1030
|
+
logger5.info({ threadId: thread.id, scope }, "thread deleted");
|
|
989
1031
|
await sessionRegistry.remove(scope);
|
|
990
1032
|
});
|
|
991
1033
|
await client.login(config.discordBotToken);
|
|
@@ -993,41 +1035,41 @@ async function startGatewayClient(config, agentService, sessionRegistry, authCon
|
|
|
993
1035
|
}
|
|
994
1036
|
async function onMessage(message, config, agentService, sessionRegistry, authConfig) {
|
|
995
1037
|
if (message.author.bot) {
|
|
996
|
-
|
|
1038
|
+
logger5.debug({ messageId: message.id }, "ignored bot message");
|
|
997
1039
|
return;
|
|
998
1040
|
}
|
|
999
1041
|
if (message.system) {
|
|
1000
|
-
|
|
1042
|
+
logger5.debug({ messageId: message.id }, "ignored system message");
|
|
1001
1043
|
return;
|
|
1002
1044
|
}
|
|
1003
1045
|
const scope = resolveScope(message);
|
|
1004
1046
|
if (scope === null) {
|
|
1005
|
-
|
|
1047
|
+
logger5.debug({
|
|
1006
1048
|
messageId: message.id,
|
|
1007
1049
|
channelType: message.channel.type
|
|
1008
|
-
});
|
|
1050
|
+
}, "unsupported channel type, ignoring");
|
|
1009
1051
|
return;
|
|
1010
1052
|
}
|
|
1011
1053
|
if (!isAuthorized(message, scope, authConfig)) {
|
|
1012
|
-
|
|
1054
|
+
logger5.debug({
|
|
1013
1055
|
messageId: message.id,
|
|
1014
1056
|
authorId: message.author.id,
|
|
1015
1057
|
scope
|
|
1016
|
-
});
|
|
1058
|
+
}, "unauthorized");
|
|
1017
1059
|
return;
|
|
1018
1060
|
}
|
|
1019
1061
|
const content = message.content.trim();
|
|
1020
1062
|
if (!content) {
|
|
1021
|
-
|
|
1063
|
+
logger5.debug({ messageId: message.id }, "ignored empty message");
|
|
1022
1064
|
return;
|
|
1023
1065
|
}
|
|
1024
1066
|
const { entry, created } = await sessionRegistry.getOrCreate(scope);
|
|
1025
1067
|
const { session, promptQueue } = entry;
|
|
1026
1068
|
if (created && scope.startsWith("thread:") && message.channel.isThread()) {
|
|
1027
|
-
|
|
1069
|
+
logger5.info({
|
|
1028
1070
|
scope,
|
|
1029
1071
|
threadName: message.channel.name
|
|
1030
|
-
});
|
|
1072
|
+
}, "new thread session");
|
|
1031
1073
|
}
|
|
1032
1074
|
let typingInterval = null;
|
|
1033
1075
|
if (message.channel.isSendable()) {
|
|
@@ -1041,7 +1083,7 @@ async function onMessage(message, config, agentService, sessionRegistry, authCon
|
|
|
1041
1083
|
if (commandResult.handled) {
|
|
1042
1084
|
stopTypingInterval(typingInterval);
|
|
1043
1085
|
if (commandResult.archive && scope.startsWith("thread:")) {
|
|
1044
|
-
|
|
1086
|
+
logger5.info({ scope }, "archiving thread");
|
|
1045
1087
|
const archiveChannel = message.channel;
|
|
1046
1088
|
if (archiveChannel.isSendable()) {
|
|
1047
1089
|
await archiveChannel.send(commandResult.response ?? "Archiving...");
|
|
@@ -1051,16 +1093,16 @@ async function onMessage(message, config, agentService, sessionRegistry, authCon
|
|
|
1051
1093
|
await archiveChannel.setArchived(true);
|
|
1052
1094
|
}
|
|
1053
1095
|
} catch (error) {
|
|
1054
|
-
|
|
1096
|
+
logger5.error({ error }, "failed to archive thread");
|
|
1055
1097
|
}
|
|
1056
1098
|
await sessionRegistry.remove(scope);
|
|
1057
1099
|
return;
|
|
1058
1100
|
}
|
|
1059
|
-
|
|
1101
|
+
logger5.info({
|
|
1060
1102
|
messageId: message.id,
|
|
1061
1103
|
command: content,
|
|
1062
1104
|
hasResponse: Boolean(commandResult.response)
|
|
1063
|
-
});
|
|
1105
|
+
}, "command handled");
|
|
1064
1106
|
if (commandResult.response) {
|
|
1065
1107
|
await sendReply(message, commandResult.response);
|
|
1066
1108
|
}
|
|
@@ -1068,34 +1110,37 @@ async function onMessage(message, config, agentService, sessionRegistry, authCon
|
|
|
1068
1110
|
}
|
|
1069
1111
|
if (!message.channel.isSendable()) {
|
|
1070
1112
|
stopTypingInterval(typingInterval);
|
|
1071
|
-
|
|
1113
|
+
logger5.debug({ messageId: message.id }, "channel not sendable");
|
|
1072
1114
|
return;
|
|
1073
1115
|
}
|
|
1074
1116
|
const queuePosition = promptQueue.getSnapshot().pending;
|
|
1075
|
-
|
|
1117
|
+
logger5.debug({
|
|
1076
1118
|
scope,
|
|
1077
1119
|
messageId: message.id,
|
|
1078
1120
|
queuePosition
|
|
1079
|
-
});
|
|
1121
|
+
}, "enqueue request");
|
|
1080
1122
|
if (queuePosition > 0) {
|
|
1081
1123
|
await sendReply(message, `Queued. ${queuePosition} request(s) ahead of this one.`);
|
|
1082
1124
|
}
|
|
1083
1125
|
const response = await promptQueue.enqueue(async () => {
|
|
1084
|
-
|
|
1126
|
+
logger5.debug({
|
|
1127
|
+
messageId: message.id,
|
|
1128
|
+
scope
|
|
1129
|
+
}, "processing message");
|
|
1085
1130
|
const promptContent = buildDiscordPromptContent(message, scope, content, config);
|
|
1086
|
-
console.log("[gateway:debug] prompt content", promptContent);
|
|
1087
1131
|
const transformedPrompt = await config.promptTransform(promptContent);
|
|
1088
1132
|
return collectReply(session, transformedPrompt, {
|
|
1089
1133
|
logPrefix: `[agent:${session.sessionId}]`
|
|
1090
1134
|
});
|
|
1091
1135
|
});
|
|
1092
1136
|
stopTypingInterval(typingInterval);
|
|
1093
|
-
|
|
1137
|
+
logger5.info({
|
|
1138
|
+
direction: "OUT",
|
|
1094
1139
|
scope,
|
|
1095
1140
|
messageId: message.id,
|
|
1096
1141
|
responseLength: response.length,
|
|
1097
|
-
|
|
1098
|
-
});
|
|
1142
|
+
response
|
|
1143
|
+
}, "response ready");
|
|
1099
1144
|
await sendReply(message, response);
|
|
1100
1145
|
}
|
|
1101
1146
|
|
|
@@ -1151,6 +1196,7 @@ function sessionDirForScope(agentDir, scope) {
|
|
|
1151
1196
|
}
|
|
1152
1197
|
throw new Error(`Unknown session scope: ${scope}`);
|
|
1153
1198
|
}
|
|
1199
|
+
var logger6 = createModuleLogger("session-registry");
|
|
1154
1200
|
|
|
1155
1201
|
class SessionRegistry {
|
|
1156
1202
|
scopes = new Map;
|
|
@@ -1172,11 +1218,11 @@ class SessionRegistry {
|
|
|
1172
1218
|
createdAt: new Date
|
|
1173
1219
|
};
|
|
1174
1220
|
this.scopes.set(scope, entry);
|
|
1175
|
-
|
|
1221
|
+
logger6.debug({
|
|
1176
1222
|
scope,
|
|
1177
1223
|
sessionDir,
|
|
1178
1224
|
sessionId: session.sessionId
|
|
1179
|
-
});
|
|
1225
|
+
}, "scope registered");
|
|
1180
1226
|
return { entry, created: true };
|
|
1181
1227
|
}
|
|
1182
1228
|
async remove(scope) {
|
|
@@ -1184,7 +1230,7 @@ class SessionRegistry {
|
|
|
1184
1230
|
if (!entry) {
|
|
1185
1231
|
return;
|
|
1186
1232
|
}
|
|
1187
|
-
|
|
1233
|
+
logger6.debug({ scope }, "removing scope");
|
|
1188
1234
|
await entry.session.abort();
|
|
1189
1235
|
entry.session.dispose();
|
|
1190
1236
|
this.scopes.delete(scope);
|
|
@@ -1196,7 +1242,7 @@ class SessionRegistry {
|
|
|
1196
1242
|
return Array.from(this.scopes.keys());
|
|
1197
1243
|
}
|
|
1198
1244
|
async shutdownAll() {
|
|
1199
|
-
|
|
1245
|
+
logger6.info({ count: this.scopes.size }, "shutting down all scopes");
|
|
1200
1246
|
const scopes = Array.from(this.scopes.keys());
|
|
1201
1247
|
for (const scope of scopes) {
|
|
1202
1248
|
await this.remove(scope);
|
|
@@ -1205,12 +1251,13 @@ class SessionRegistry {
|
|
|
1205
1251
|
}
|
|
1206
1252
|
|
|
1207
1253
|
// src/index.ts
|
|
1254
|
+
var logger7 = createModuleLogger("index");
|
|
1208
1255
|
async function startDiscordGateway(config) {
|
|
1209
1256
|
const resolvedConfig = resolveGatewayConfig(config);
|
|
1210
1257
|
const agentService = new AgentService(resolvedConfig);
|
|
1211
|
-
|
|
1258
|
+
logger7.info("initializing agent service");
|
|
1212
1259
|
await agentService.initialize();
|
|
1213
|
-
|
|
1260
|
+
logger7.info(agentService.getStatus(), "agent ready");
|
|
1214
1261
|
const authConfig = {
|
|
1215
1262
|
discordAllowedUserId: resolvedConfig.discordAllowedUserId,
|
|
1216
1263
|
discordAllowedForumChannelIds: resolvedConfig.discordAllowedForumChannelIds,
|
|
@@ -1241,10 +1288,10 @@ function createGatewayStopHandler(client, agentService, sessionRegistry, config)
|
|
|
1241
1288
|
return;
|
|
1242
1289
|
}
|
|
1243
1290
|
stopped = true;
|
|
1244
|
-
|
|
1291
|
+
logger7.info({
|
|
1245
1292
|
cwd: config.cwd,
|
|
1246
1293
|
agentDir: config.agentDir
|
|
1247
|
-
});
|
|
1294
|
+
}, "stopping discord gateway");
|
|
1248
1295
|
client.destroy();
|
|
1249
1296
|
await sessionRegistry.shutdownAll();
|
|
1250
1297
|
await agentService.shutdown();
|
|
@@ -1252,9 +1299,9 @@ function createGatewayStopHandler(client, agentService, sessionRegistry, config)
|
|
|
1252
1299
|
}
|
|
1253
1300
|
function registerSignalHandlers(stop) {
|
|
1254
1301
|
const handleSignal = (signal) => {
|
|
1255
|
-
|
|
1302
|
+
logger7.info({ signal }, "received signal");
|
|
1256
1303
|
stop().finally(() => {
|
|
1257
|
-
|
|
1304
|
+
logger7.info("done");
|
|
1258
1305
|
process.exit(0);
|
|
1259
1306
|
});
|
|
1260
1307
|
};
|
package/dist/logger.d.ts
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@friendlyrobot/discord-pi-agent",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.1",
|
|
4
4
|
"description": "Reusable Discord gateway bridge for persistent pi agent sessions",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -40,11 +40,13 @@
|
|
|
40
40
|
"discord.js": "^14.26.4",
|
|
41
41
|
"dotenv": "^17.4.2",
|
|
42
42
|
"marked": "^18.0.3",
|
|
43
|
+
"pino": "^10.3.1",
|
|
44
|
+
"pino-pretty": "^13.1.3",
|
|
43
45
|
"prettier": "^3.8.3"
|
|
44
46
|
},
|
|
45
47
|
"devDependencies": {
|
|
46
48
|
"@types/node": "^25.6.0",
|
|
47
|
-
"@typescript/native-preview": "^7.0.0-dev.
|
|
49
|
+
"@typescript/native-preview": "^7.0.0-dev.20260506.1",
|
|
48
50
|
"@vitest/ui": "^4.1.5",
|
|
49
51
|
"vitest": "^4.1.5"
|
|
50
52
|
}
|