@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 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
- console.error("[markdown-table-transformer] Prettier formatting failed:", error);
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
- console.log(`${logPrefix} prompt start`, { prompt });
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
- console.log(`${logPrefix} tool start`, {
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
- console.log(`${logPrefix} tool end`, {
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
- console.log(`${logPrefix} agent end`, {
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
- console.log(`${logPrefix} prompt done`, {
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
- console.log("[DEBUG] Table transformation comparison:", `
125
- === RAW finalText ===
126
- ` + finalText + `
127
- === TRANSFORMED ===
128
- ` + transformed + `
129
- === END ===`);
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
- console.log("[agent] config", {
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
- console.log("[agent] resources loaded", {
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
- console.log("[agent] scoped session created", {
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
- console.log("[agent] session ready", {
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
- console.log("[agent] available models", {
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
- console.log("[agent] model already selected", {
366
+ logger4.debug({
328
367
  model: `${desiredModel.provider}/${desiredModel.id}`
329
- });
368
+ }, "model already selected");
330
369
  return;
331
370
  }
332
- console.log("[agent] switching model", {
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
- console.log("[agent] thinking level applied", {
392
+ logger4.debug({
354
393
  level: this.config.thinkingLevel
355
- });
394
+ }, "thinking level applied");
356
395
  } else {
357
- console.log("[agent] thinking level not available for model", {
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
- console.log("[gateway] reply skipped, channel not sendable", {
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
- console.log("[gateway] sending reply", {
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
- console.error("[gateway] send reply failed", {
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
- console.log(`[gateway] logged in as ${readyClient.user.tag}`);
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
- console.log("[gateway] sent startup dm", {
1006
+ logger5.info({
966
1007
  userId: authConfig.discordAllowedUserId
967
- });
1008
+ }, "sent startup dm");
968
1009
  } catch (error) {
969
- console.error("[gateway] failed to send startup dm", error);
1010
+ logger5.error({ error }, "failed to send startup dm");
970
1011
  }
971
1012
  });
972
1013
  client.on(Events.MessageCreate, async (message) => {
973
- console.log("[gateway] message received", {
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.slice(0, 200)
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
- console.error("[gateway] message handling failed", error);
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
- console.log("[gateway] thread deleted", { threadId: thread.id, scope });
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
- console.log("[gateway] ignored bot message", { messageId: message.id });
1038
+ logger5.debug({ messageId: message.id }, "ignored bot message");
997
1039
  return;
998
1040
  }
999
1041
  if (message.system) {
1000
- console.log("[gateway] ignored system message", { messageId: message.id });
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
- console.log("[gateway] unsupported channel type, ignoring", {
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
- console.log("[gateway] unauthorized", {
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
- console.log("[gateway] ignored empty message", { messageId: message.id });
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
- console.log("[gateway] new thread session", {
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
- console.log("[gateway] archiving thread", { scope });
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
- console.error("[gateway] failed to archive thread", error);
1096
+ logger5.error({ error }, "failed to archive thread");
1055
1097
  }
1056
1098
  await sessionRegistry.remove(scope);
1057
1099
  return;
1058
1100
  }
1059
- console.log("[gateway] command handled", {
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
- console.log("[gateway] channel not sendable", { messageId: message.id });
1113
+ logger5.debug({ messageId: message.id }, "channel not sendable");
1072
1114
  return;
1073
1115
  }
1074
1116
  const queuePosition = promptQueue.getSnapshot().pending;
1075
- console.log("[queue] enqueue request", {
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
- console.log(`[queue] processing message ${message.id} in scope ${scope}`);
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
- console.log("[gateway] response ready", {
1137
+ logger5.info({
1138
+ direction: "OUT",
1094
1139
  scope,
1095
1140
  messageId: message.id,
1096
1141
  responseLength: response.length,
1097
- preview: response.slice(0, 200)
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
- console.log("[session-registry] scope registered", {
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
- console.log("[session-registry] removing scope", { scope });
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
- console.log("[session-registry] shutting down all scopes", this.scopes.size);
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
- console.log("[gateway] initializing agent service");
1258
+ logger7.info("initializing agent service");
1212
1259
  await agentService.initialize();
1213
- console.log("[gateway] agent ready", agentService.getStatus());
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
- console.log("[shutdown] stopping discord gateway", {
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
- console.log(`[shutdown] received ${signal}`);
1302
+ logger7.info({ signal }, "received signal");
1256
1303
  stop().finally(() => {
1257
- console.log("[shutdown] done");
1304
+ logger7.info("done");
1258
1305
  process.exit(0);
1259
1306
  });
1260
1307
  };
@@ -0,0 +1,3 @@
1
+ import { type Logger } from "pino";
2
+ export declare const logger: Logger<never, boolean>;
3
+ export declare function createModuleLogger(moduleName: string): Logger;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@friendlyrobot/discord-pi-agent",
3
- "version": "0.6.1",
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.20260504.1",
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
  }