@friendlyrobot/discord-pi-agent 0.17.0 → 0.18.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/README.md CHANGED
@@ -16,6 +16,7 @@ Reusable Discord gateway for persistent pi agent sessions — DM and forum chann
16
16
  - `!help`
17
17
  - `!status`
18
18
  - `!thinking`
19
+ - `!model`
19
20
  - `!compact`
20
21
  - `!reload`
21
22
  - `!reset-session`
@@ -1,9 +1,9 @@
1
1
  import { type Message } from "discord.js";
2
- import type { GatewayAuthConfig } from "./discord-gateway-client";
2
+ import type { GatewayAccessConfig } from "./types";
3
3
  export declare function getAuthorDisplayName(message: Message): string;
4
4
  /**
5
5
  * Determine the session scope from an incoming message.
6
6
  * Returns null for unsupported channel types (silently ignored).
7
7
  */
8
8
  export declare function resolveMessageScope(message: Message): string | null;
9
- export declare function isAuthorizedMessage(message: Message, scope: string, authConfig: GatewayAuthConfig): boolean;
9
+ export declare function isAuthorizedMessage(message: Message, scope: string, accessConfig: GatewayAccessConfig): boolean;
@@ -1,11 +1,5 @@
1
1
  import { Client } from "discord.js";
2
2
  import type { AgentService } from "./agent-service";
3
3
  import type { SessionRegistry } from "./session-registry";
4
- import type { ResolvedDiscordGatewayConfig } from "./types";
5
- export type GatewayAuthConfig = {
6
- discordAllowedUserId: string;
7
- discordAllowedForumChannelIds: string[];
8
- discordAllowedUserIds: string[];
9
- startupMessage: string | false;
10
- };
11
- export declare function startGatewayClient(config: ResolvedDiscordGatewayConfig, agentService: AgentService, sessionRegistry: SessionRegistry, authConfig: GatewayAuthConfig): Promise<Client>;
4
+ import type { GatewayAccessConfig, ResolvedDiscordGatewayConfig } from "./types";
5
+ export declare function startGatewayClient(config: ResolvedDiscordGatewayConfig, agentService: AgentService, sessionRegistry: SessionRegistry, accessConfig: GatewayAccessConfig): Promise<Client>;
@@ -1,6 +1,5 @@
1
1
  import type { Message } from "discord.js";
2
2
  import type { AgentService } from "./agent-service";
3
3
  import type { SessionRegistry } from "./session-registry";
4
- import type { ResolvedDiscordGatewayConfig } from "./types";
5
- import type { GatewayAuthConfig } from "./discord-gateway-client";
6
- export declare function handleDiscordMessage(message: Message, config: ResolvedDiscordGatewayConfig, agentService: AgentService, sessionRegistry: SessionRegistry, authConfig: GatewayAuthConfig): Promise<void>;
4
+ import type { GatewayAccessConfig, ResolvedDiscordGatewayConfig } from "./types";
5
+ export declare function handleDiscordMessage(message: Message, config: ResolvedDiscordGatewayConfig, agentService: AgentService, sessionRegistry: SessionRegistry, accessConfig: GatewayAccessConfig): Promise<void>;
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import type { DiscordGateway, DiscordGatewayConfig } from "./types";
2
2
  export { buildDiscordMessageContextPrompt, formatDiscordPromptTime, type DiscordMessageContextPromptOptions, type DiscordPromptScope, type DiscordPromptTimeFormatOptions, } from "./prompt-context";
3
3
  export { loadDiscordGatewayConfigFromEnv, resolveConfig } from "./config";
4
- export type { AgentStatus, DiscordGateway, DiscordGatewayConfig, PromptTransform, ResolvedDiscordGatewayConfig, } from "./types";
4
+ export type { AgentStatus, DiscordGateway, DiscordGatewayConfig, PromptTransform, ResolvedDiscordGatewayConfig, GatewayAccessConfig, } from "./types";
5
5
  /**
6
6
  * Start the unified Discord gateway. Supports DM and forum thread sessions
7
7
  * out of the box. Set discordAllowedForumChannelIds to enable forum support.
package/dist/index.js CHANGED
@@ -557,11 +557,11 @@ class AgentService {
557
557
  import path2 from "node:path";
558
558
  import dotenv from "dotenv";
559
559
  function resolveConfig(config) {
560
- const discordAllowedUserId = readRequiredValue("discordAllowedUserId", config.discordAllowedUserId);
560
+ const discordAllowedUserId = requireNonEmptyConfigValue("discordAllowedUserId", config.discordAllowedUserId);
561
561
  return {
562
- discordBotToken: readRequiredValue("discordBotToken", config.discordBotToken),
562
+ discordBotToken: requireNonEmptyConfigValue("discordBotToken", config.discordBotToken),
563
563
  discordAllowedUserId,
564
- cwd: readRequiredValue("cwd", config.cwd),
564
+ cwd: requireNonEmptyConfigValue("cwd", config.cwd),
565
565
  agentDir: config.agentDir?.trim() || path2.join(config.cwd, ".pi-agent"),
566
566
  modelProvider: config.modelProvider?.trim() || "openrouter",
567
567
  modelId: config.modelId?.trim() || "anthropic/claude-3.5-haiku",
@@ -596,7 +596,7 @@ function loadDiscordGatewayConfigFromEnv(overrides = {}) {
596
596
  discordAllowedUserIds: overrides.discordAllowedUserIds ?? parseStringArrayFromEnv("DISCORD_ALLOWED_USER_IDS")
597
597
  });
598
598
  }
599
- function readRequiredValue(name, value) {
599
+ function requireNonEmptyConfigValue(name, value) {
600
600
  const trimmedValue = value.trim();
601
601
  if (!trimmedValue) {
602
602
  throw new Error(`Missing required config value: ${name}`);
@@ -651,7 +651,7 @@ import {
651
651
  Partials
652
652
  } from "discord.js";
653
653
 
654
- // src/commands.ts
654
+ // src/session-commands.ts
655
655
  function getSessionStatusText(session, promptQueue, extras) {
656
656
  const model = session.model ? `${session.model.provider}/${session.model.id}` : "(no model selected)";
657
657
  const contextUsage = session.getContextUsage();
@@ -881,7 +881,7 @@ var commandHandlers = [
881
881
  handleReloadCommand,
882
882
  handleResetSessionCommand
883
883
  ];
884
- async function executeCommand(input, context) {
884
+ async function executeSessionCommand(input, context) {
885
885
  const trimmedInput = input.trim();
886
886
  if (!trimmedInput.startsWith("!")) {
887
887
  return { handled: false };
@@ -1055,9 +1055,9 @@ function resolveMessageScope(message) {
1055
1055
  }
1056
1056
  return null;
1057
1057
  }
1058
- function isAuthorizedMessage(message, scope, authConfig) {
1058
+ function isAuthorizedMessage(message, scope, accessConfig) {
1059
1059
  if (scope === "dm") {
1060
- return message.author.id === authConfig.discordAllowedUserId;
1060
+ return message.author.id === accessConfig.discordAllowedUserId;
1061
1061
  }
1062
1062
  if (scope.startsWith("thread:")) {
1063
1063
  const channel = message.channel;
@@ -1065,10 +1065,10 @@ function isAuthorizedMessage(message, scope, authConfig) {
1065
1065
  return false;
1066
1066
  }
1067
1067
  const parentId = channel.parentId;
1068
- if (!parentId || !authConfig.discordAllowedForumChannelIds.includes(parentId)) {
1068
+ if (!parentId || !accessConfig.discordAllowedForumChannelIds.includes(parentId)) {
1069
1069
  return false;
1070
1070
  }
1071
- return authConfig.discordAllowedUserIds.includes(message.author.id);
1071
+ return accessConfig.discordAllowedUserIds.includes(message.author.id);
1072
1072
  }
1073
1073
  return false;
1074
1074
  }
@@ -1150,8 +1150,8 @@ async function sendReply(message, text) {
1150
1150
  }
1151
1151
  }
1152
1152
 
1153
- // src/image-description.ts
1154
- var logger8 = createModuleLogger("image-description");
1153
+ // src/media-description.ts
1154
+ var logger8 = createModuleLogger("media-description");
1155
1155
  async function describeMediaAttachment(agentService, imageData, mimeType, userText, visionModel) {
1156
1156
  const session = await agentService.createTemporarySession();
1157
1157
  await session.setModel(visionModel);
@@ -1455,7 +1455,7 @@ function buildDiscordPromptContent(message, scope, content, config) {
1455
1455
  forumChannelId: isThread ? message.channel.parentId : undefined
1456
1456
  });
1457
1457
  }
1458
- async function handleDiscordMessage(message, config, agentService, sessionRegistry, authConfig) {
1458
+ async function handleDiscordMessage(message, config, agentService, sessionRegistry, accessConfig) {
1459
1459
  if (message.author.bot) {
1460
1460
  logger11.debug("ignored bot message");
1461
1461
  return;
@@ -1472,7 +1472,7 @@ async function handleDiscordMessage(message, config, agentService, sessionRegist
1472
1472
  }, "unsupported channel type, ignoring");
1473
1473
  return;
1474
1474
  }
1475
- if (!isAuthorizedMessage(message, scope, authConfig)) {
1475
+ if (!isAuthorizedMessage(message, scope, accessConfig)) {
1476
1476
  logger11.debug({
1477
1477
  messageId: message.id,
1478
1478
  authorId: message.author.id,
@@ -1516,7 +1516,7 @@ ${attachment.content}`;
1516
1516
  threadName: message.channel.name
1517
1517
  }, "new thread session");
1518
1518
  }
1519
- const commandResult = await executeCommand(content, {
1519
+ const commandResult = await executeSessionCommand(content, {
1520
1520
  agentService,
1521
1521
  promptQueue,
1522
1522
  session
@@ -1586,7 +1586,7 @@ ${attachment.content}`;
1586
1586
 
1587
1587
  // src/discord-gateway-client.ts
1588
1588
  var logger12 = createModuleLogger("discord-gateway");
1589
- async function startGatewayClient(config, agentService, sessionRegistry, authConfig) {
1589
+ async function startGatewayClient(config, agentService, sessionRegistry, accessConfig) {
1590
1590
  const client = new Client({
1591
1591
  intents: [
1592
1592
  GatewayIntentBits.DirectMessages,
@@ -1598,15 +1598,15 @@ async function startGatewayClient(config, agentService, sessionRegistry, authCon
1598
1598
  });
1599
1599
  client.once(Events.ClientReady, async (readyClient) => {
1600
1600
  logger12.info({ userTag: readyClient.user.tag }, "logged in");
1601
- if (!authConfig.startupMessage) {
1601
+ if (!accessConfig.startupMessage) {
1602
1602
  return;
1603
1603
  }
1604
1604
  try {
1605
- const user = await readyClient.users.fetch(authConfig.discordAllowedUserId);
1605
+ const user = await readyClient.users.fetch(accessConfig.discordAllowedUserId);
1606
1606
  const dmChannel = await user.createDM();
1607
- await dmChannel.send(authConfig.startupMessage);
1607
+ await dmChannel.send(accessConfig.startupMessage);
1608
1608
  logger12.info({
1609
- userId: authConfig.discordAllowedUserId
1609
+ userId: accessConfig.discordAllowedUserId
1610
1610
  }, "sent startup dm");
1611
1611
  } catch (error) {
1612
1612
  logger12.error({ error }, "failed to send startup dm");
@@ -1614,7 +1614,7 @@ async function startGatewayClient(config, agentService, sessionRegistry, authCon
1614
1614
  });
1615
1615
  client.on(Events.MessageCreate, async (message) => {
1616
1616
  try {
1617
- await handleDiscordMessage(message, config, agentService, sessionRegistry, authConfig);
1617
+ await handleDiscordMessage(message, config, agentService, sessionRegistry, accessConfig);
1618
1618
  } catch (error) {
1619
1619
  logger12.error({ error, direction: "IN" }, "message handling failed");
1620
1620
  await sendReply(message, "The bot hit an error while handling that message.");
@@ -1743,14 +1743,14 @@ async function startDiscordGateway(config) {
1743
1743
  logger14.info("initializing agent service");
1744
1744
  await agentService.initialize();
1745
1745
  logger14.info(agentService.getStatus(), "agent ready");
1746
- const authConfig = {
1746
+ const accessConfig = {
1747
1747
  discordAllowedUserId: resolvedConfig.discordAllowedUserId,
1748
1748
  discordAllowedForumChannelIds: resolvedConfig.discordAllowedForumChannelIds,
1749
1749
  discordAllowedUserIds: resolvedConfig.discordAllowedUserIds,
1750
1750
  startupMessage: resolvedConfig.startupMessage
1751
1751
  };
1752
1752
  const sessionRegistry = new SessionRegistry(agentService);
1753
- const client = await startGatewayClient(resolvedConfig, agentService, sessionRegistry, authConfig);
1753
+ const client = await startGatewayClient(resolvedConfig, agentService, sessionRegistry, accessConfig);
1754
1754
  const stop = createGatewayStopHandler(client, agentService, sessionRegistry, resolvedConfig);
1755
1755
  if (resolvedConfig.shutdownOnSignals) {
1756
1756
  registerSignalHandlers(stop);
@@ -12,4 +12,4 @@ export type CommandContext = {
12
12
  promptQueue: PromptQueue;
13
13
  session?: AgentSession;
14
14
  };
15
- export declare function executeCommand(input: string, context: CommandContext): Promise<CommandResult>;
15
+ export declare function executeSessionCommand(input: string, context: CommandContext): Promise<CommandResult>;
@@ -2,7 +2,7 @@ import type { AgentSession } from "@earendil-works/pi-coding-agent";
2
2
  import type { AgentService } from "./agent-service";
3
3
  import { PromptQueue } from "./prompt-queue";
4
4
  export type SessionScope = string;
5
- export type ScopeEntry = {
5
+ export type ScopedSessionEntry = {
6
6
  session: AgentSession;
7
7
  promptQueue: PromptQueue;
8
8
  createdAt: Date;
@@ -19,11 +19,11 @@ export declare class SessionRegistry {
19
19
  private readonly agentService;
20
20
  constructor(agentService: AgentService);
21
21
  getOrCreate(scope: SessionScope): Promise<{
22
- entry: ScopeEntry;
22
+ entry: ScopedSessionEntry;
23
23
  created: boolean;
24
24
  }>;
25
25
  remove(scope: SessionScope): Promise<void>;
26
- get(scope: SessionScope): ScopeEntry | undefined;
26
+ get(scope: SessionScope): ScopedSessionEntry | undefined;
27
27
  getScopes(): SessionScope[];
28
28
  shutdownAll(): Promise<void>;
29
29
  }
package/dist/types.d.ts CHANGED
@@ -57,6 +57,12 @@ export type AgentStatus = {
57
57
  contextUsage: ContextUsageStatus | undefined;
58
58
  thinkingInfo: string;
59
59
  };
60
+ export type GatewayAccessConfig = {
61
+ discordAllowedUserId: string;
62
+ discordAllowedForumChannelIds: string[];
63
+ discordAllowedUserIds: string[];
64
+ startupMessage: string | false;
65
+ };
60
66
  export type DiscordGateway = {
61
67
  client: Client;
62
68
  stop: () => Promise<void>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@friendlyrobot/discord-pi-agent",
3
- "version": "0.17.0",
3
+ "version": "0.18.0",
4
4
  "description": "Reusable Discord gateway for persistent pi agent sessions",
5
5
  "license": "MIT",
6
6
  "type": "module",