@knowsuchagency/fulcrum 2.16.1 → 2.16.2

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/bin/fulcrum.js CHANGED
@@ -45795,7 +45795,7 @@ var init_email = __esm(() => {
45795
45795
  var ChannelSchema, registerAssistantTools = (server, client) => {
45796
45796
  server.tool("message", "Send a message to a messaging channel (WhatsApp, Discord, Telegram, Slack). Use this to reply to messages or send proactive communications. For email, use create_gmail_draft instead.", {
45797
45797
  channel: ChannelSchema.describe("Target channel: whatsapp, discord, telegram, slack, or all"),
45798
- to: exports_external.string().describe("Recipient (email address, phone number, or channel ID)"),
45798
+ to: exports_external.optional(exports_external.string()).describe("Recipient identifier. Optional \u2014 if omitted, auto-resolves to the channel's primary user."),
45799
45799
  body: exports_external.string().describe("Message content"),
45800
45800
  subject: exports_external.optional(exports_external.string()).describe("Email subject (for email channel only)"),
45801
45801
  replyToMessageId: exports_external.optional(exports_external.string()).describe("Message ID to reply to (for threading)"),
@@ -46247,7 +46247,7 @@ async function runMcpServer(urlOverride, portOverride) {
46247
46247
  const client = new FulcrumClient(urlOverride, portOverride);
46248
46248
  const server = new McpServer({
46249
46249
  name: "fulcrum",
46250
- version: "2.16.1"
46250
+ version: "2.16.2"
46251
46251
  });
46252
46252
  registerTools(server, client);
46253
46253
  const transport = new StdioServerTransport;
@@ -48596,7 +48596,7 @@ var marketplace_default = `{
48596
48596
  "name": "fulcrum",
48597
48597
  "source": "./",
48598
48598
  "description": "Task orchestration for Claude Code",
48599
- "version": "2.16.1",
48599
+ "version": "2.16.2",
48600
48600
  "skills": [
48601
48601
  "./skills/fulcrum"
48602
48602
  ],
@@ -49784,7 +49784,7 @@ function compareVersions(v1, v2) {
49784
49784
  var package_default = {
49785
49785
  name: "@knowsuchagency/fulcrum",
49786
49786
  private: true,
49787
- version: "2.16.1",
49787
+ version: "2.16.2",
49788
49788
  description: "Harness Attention. Orchestrate Agents. Ship.",
49789
49789
  license: "PolyForm-Perimeter-1.0.0",
49790
49790
  type: "module",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@knowsuchagency/fulcrum",
3
- "version": "2.16.1",
3
+ "version": "2.16.2",
4
4
  "description": "Harness Attention. Orchestrate Agents. Ship.",
5
5
  "license": "PolyForm-Perimeter-1.0.0",
6
6
  "repository": {
package/server/index.js CHANGED
@@ -71223,7 +71223,7 @@ You have access to Fulcrum's MCP tools. Use them proactively to help users.
71223
71223
  - \`fetch_emails\` - Fetch specific emails by IMAP UID
71224
71224
 
71225
71225
  **Assistant Tools (Proactive Agent):**
71226
- - \`message\` - Send a message to a channel (email, whatsapp)
71226
+ - \`message\` - Send a message to a channel (whatsapp, discord, telegram, slack). The \`to\` param is optional \u2014 omit it and the recipient auto-resolves to the channel's primary user
71227
71227
  - \`get_last_sweep\` - Check when last sweep ran
71228
71228
  - Use \`memory_store\` with tag \`actionable\` to track things that need attention
71229
71229
  - Use \`search\` with \`memoryTags: ["actionable"]\` to review tracked items
@@ -1201037,7 +1201037,7 @@ ${context.metadata?.threadId ? `**Thread ID**: ${context.metadata.threadId}` : "
1201037
1201037
 
1201038
1201038
  - **You don't need to store a memory for every message** - only for things that need tracking/follow-up
1201039
1201039
  - Simple greetings, questions, and conversations can be answered directly without any memory tracking
1201040
- - For replies, use the \`message\` tool with: channel="${context.channel}", to="${context.sender}"${context.metadata?.messageId ? `, replyToMessageId="${context.metadata.messageId}"` : ""}
1201040
+ - For replies, use the \`message\` tool with: channel="${context.channel}", body="your reply"${context.metadata?.messageId ? `, replyToMessageId="${context.metadata.messageId}"` : ""} (the \`to\` parameter is optional \u2014 omit it and the recipient is auto-resolved)
1201041
1201041
  - Only store memories with tag \`actionable\` for requests, reminders, or things you need to remember
1201042
1201042
  - Spam, newsletters, and automated notifications should be ignored (no response)
1201043
1201043
 
@@ -1201137,7 +1201137,7 @@ You are performing your morning ritual.
1201137
1201137
  ## Output Channels
1201138
1201138
 
1201139
1201139
  Use the \`list_messaging_channels\` tool to discover which messaging channels are available and connected.
1201140
- Then use the \`message\` tool to send your briefing to the connected channels.`;
1201140
+ Then use the \`message\` tool to send your briefing \u2014 just specify \`channel\` and \`body\`, the recipient is auto-resolved.`;
1201141
1201141
  }
1201142
1201142
  return `## Evening Ritual
1201143
1201143
 
@@ -1201148,7 +1201148,7 @@ You are performing your evening ritual.
1201148
1201148
  ## Output Channels
1201149
1201149
 
1201150
1201150
  Use the \`list_messaging_channels\` tool to discover which messaging channels are available and connected.
1201151
- Then use the \`message\` tool to send your summary to the connected channels.`;
1201151
+ Then use the \`message\` tool to send your summary \u2014 just specify \`channel\` and \`body\`, the recipient is auto-resolved.`;
1201152
1201152
  }
1201153
1201153
  function getFormattingGuide(channelType) {
1201154
1201154
  switch (channelType) {
@@ -1201994,7 +1201994,41 @@ var init_email3 = __esm(() => {
1201994
1201994
  });
1201995
1201995
 
1201996
1201996
  // server/services/channels/index.ts
1201997
+ function resolveRecipient(channel) {
1201998
+ switch (channel) {
1201999
+ case "whatsapp": {
1202000
+ const row = db2.select({ displayName: messagingConnections.displayName }).from(messagingConnections).where(eq(messagingConnections.channelType, "whatsapp")).get();
1202001
+ return row?.displayName || null;
1202002
+ }
1202003
+ case "slack": {
1202004
+ const row = db2.select({ channelUserId: messagingSessionMappings.channelUserId }).from(messagingSessionMappings).where(eq(messagingSessionMappings.connectionId, SLACK_CONNECTION_ID)).orderBy(desc(messagingSessionMappings.lastMessageAt)).limit(1).get();
1202005
+ return row?.channelUserId || null;
1202006
+ }
1202007
+ case "discord": {
1202008
+ const row = db2.select({ channelUserId: messagingSessionMappings.channelUserId }).from(messagingSessionMappings).where(eq(messagingSessionMappings.connectionId, DISCORD_CONNECTION_ID)).orderBy(desc(messagingSessionMappings.lastMessageAt)).limit(1).get();
1202009
+ return row?.channelUserId || null;
1202010
+ }
1202011
+ case "telegram": {
1202012
+ const row = db2.select({ channelUserId: messagingSessionMappings.channelUserId }).from(messagingSessionMappings).where(eq(messagingSessionMappings.connectionId, TELEGRAM_CONNECTION_ID)).orderBy(desc(messagingSessionMappings.lastMessageAt)).limit(1).get();
1202013
+ return row?.channelUserId || null;
1202014
+ }
1202015
+ default:
1202016
+ return null;
1202017
+ }
1202018
+ }
1201997
1202019
  async function sendMessageToChannel(channel, to, body, options) {
1202020
+ if (!body) {
1202021
+ return { success: false, error: "Message body is required" };
1202022
+ }
1202023
+ let resolvedTo = to;
1202024
+ if (!resolvedTo) {
1202025
+ resolvedTo = resolveRecipient(channel) ?? undefined;
1202026
+ if (!resolvedTo) {
1202027
+ const channelName = channel.charAt(0).toUpperCase() + channel.slice(1);
1202028
+ return { success: false, error: `No ${channelName} recipient found \u2014 no user has messaged via ${channelName} yet` };
1202029
+ }
1202030
+ log2.messaging.debug("Auto-resolved recipient", { channel, to: resolvedTo });
1202031
+ }
1201998
1202032
  switch (channel) {
1201999
1202033
  case "email": {
1202000
1202034
  return { success: false, error: "Email sending disabled. Use Gmail drafts instead." };
@@ -1202005,20 +1202039,20 @@ async function sendMessageToChannel(channel, to, body, options) {
1202005
1202039
  return { success: false, error: "WhatsApp channel not connected" };
1202006
1202040
  }
1202007
1202041
  try {
1202008
- await sendWhatsAppMessage(to, body);
1202009
- log2.messaging.info("Sent WhatsApp message", { to });
1202042
+ await sendWhatsAppMessage(resolvedTo, body);
1202043
+ log2.messaging.info("Sent WhatsApp message", { to: resolvedTo });
1202010
1202044
  storeChannelMessage({
1202011
1202045
  channelType: "whatsapp",
1202012
1202046
  connectionId: waStatus.id,
1202013
1202047
  direction: "outgoing",
1202014
1202048
  senderId: waStatus.displayName || "self",
1202015
- recipientId: to,
1202049
+ recipientId: resolvedTo,
1202016
1202050
  content: body,
1202017
1202051
  messageTimestamp: new Date
1202018
1202052
  });
1202019
1202053
  return { success: true };
1202020
1202054
  } catch (err) {
1202021
- log2.messaging.error("Failed to send WhatsApp message", { to, error: String(err) });
1202055
+ log2.messaging.error("Failed to send WhatsApp message", { to: resolvedTo, error: String(err) });
1202022
1202056
  return { success: false, error: String(err) };
1202023
1202057
  }
1202024
1202058
  }
@@ -1202032,15 +1202066,15 @@ async function sendMessageToChannel(channel, to, body, options) {
1202032
1202066
  return { success: false, error: "Discord channel not active" };
1202033
1202067
  }
1202034
1202068
  try {
1202035
- const success2 = await discordChannel.sendMessage(to, body);
1202069
+ const success2 = await discordChannel.sendMessage(resolvedTo, body);
1202036
1202070
  if (success2) {
1202037
- log2.messaging.info("Sent Discord message", { to });
1202071
+ log2.messaging.info("Sent Discord message", { to: resolvedTo });
1202038
1202072
  storeChannelMessage({
1202039
1202073
  channelType: "discord",
1202040
1202074
  connectionId: DISCORD_CONNECTION_ID,
1202041
1202075
  direction: "outgoing",
1202042
1202076
  senderId: discordStatus.displayName || "bot",
1202043
- recipientId: to,
1202077
+ recipientId: resolvedTo,
1202044
1202078
  content: body,
1202045
1202079
  messageTimestamp: new Date
1202046
1202080
  });
@@ -1202049,7 +1202083,7 @@ async function sendMessageToChannel(channel, to, body, options) {
1202049
1202083
  return { success: false, error: "Failed to send Discord message" };
1202050
1202084
  }
1202051
1202085
  } catch (err) {
1202052
- log2.messaging.error("Failed to send Discord message", { to, error: String(err) });
1202086
+ log2.messaging.error("Failed to send Discord message", { to: resolvedTo, error: String(err) });
1202053
1202087
  return { success: false, error: String(err) };
1202054
1202088
  }
1202055
1202089
  }
@@ -1202063,15 +1202097,15 @@ async function sendMessageToChannel(channel, to, body, options) {
1202063
1202097
  return { success: false, error: "Telegram channel not active" };
1202064
1202098
  }
1202065
1202099
  try {
1202066
- const success2 = await telegramChannel.sendMessage(to, body);
1202100
+ const success2 = await telegramChannel.sendMessage(resolvedTo, body);
1202067
1202101
  if (success2) {
1202068
- log2.messaging.info("Sent Telegram message", { to });
1202102
+ log2.messaging.info("Sent Telegram message", { to: resolvedTo });
1202069
1202103
  storeChannelMessage({
1202070
1202104
  channelType: "telegram",
1202071
1202105
  connectionId: TELEGRAM_CONNECTION_ID,
1202072
1202106
  direction: "outgoing",
1202073
1202107
  senderId: telegramStatus.displayName || "bot",
1202074
- recipientId: to,
1202108
+ recipientId: resolvedTo,
1202075
1202109
  content: body,
1202076
1202110
  messageTimestamp: new Date
1202077
1202111
  });
@@ -1202080,7 +1202114,7 @@ async function sendMessageToChannel(channel, to, body, options) {
1202080
1202114
  return { success: false, error: "Failed to send Telegram message" };
1202081
1202115
  }
1202082
1202116
  } catch (err) {
1202083
- log2.messaging.error("Failed to send Telegram message", { to, error: String(err) });
1202117
+ log2.messaging.error("Failed to send Telegram message", { to: resolvedTo, error: String(err) });
1202084
1202118
  return { success: false, error: String(err) };
1202085
1202119
  }
1202086
1202120
  }
@@ -1202095,15 +1202129,15 @@ async function sendMessageToChannel(channel, to, body, options) {
1202095
1202129
  }
1202096
1202130
  try {
1202097
1202131
  const msgMetadata = options?.slackBlocks ? { blocks: options.slackBlocks } : undefined;
1202098
- const success2 = await slackChannel.sendMessage(to, body, msgMetadata);
1202132
+ const success2 = await slackChannel.sendMessage(resolvedTo, body, msgMetadata);
1202099
1202133
  if (success2) {
1202100
- log2.messaging.info("Sent Slack message", { to, hasBlocks: !!options?.slackBlocks });
1202134
+ log2.messaging.info("Sent Slack message", { to: resolvedTo, hasBlocks: !!options?.slackBlocks });
1202101
1202135
  storeChannelMessage({
1202102
1202136
  channelType: "slack",
1202103
1202137
  connectionId: SLACK_CONNECTION_ID,
1202104
1202138
  direction: "outgoing",
1202105
1202139
  senderId: slackStatus.displayName || "bot",
1202106
- recipientId: to,
1202140
+ recipientId: resolvedTo,
1202107
1202141
  content: body,
1202108
1202142
  metadata: msgMetadata,
1202109
1202143
  messageTimestamp: new Date
@@ -1202113,7 +1202147,7 @@ async function sendMessageToChannel(channel, to, body, options) {
1202113
1202147
  return { success: false, error: "Failed to send Slack message" };
1202114
1202148
  }
1202115
1202149
  } catch (err) {
1202116
- log2.messaging.error("Failed to send Slack message", { to, error: String(err) });
1202150
+ log2.messaging.error("Failed to send Slack message", { to: resolvedTo, error: String(err) });
1202117
1202151
  return { success: false, error: String(err) };
1202118
1202152
  }
1202119
1202153
  }
@@ -1202123,6 +1202157,9 @@ async function sendMessageToChannel(channel, to, body, options) {
1202123
1202157
  }
1202124
1202158
  var init_channels = __esm(() => {
1202125
1202159
  init_logger3();
1202160
+ init_db2();
1202161
+ init_schema();
1202162
+ init_drizzle_orm();
1202126
1202163
  init_channel_manager();
1202127
1202164
  init_message_storage();
1202128
1202165
  init_whatsapp();
@@ -1259307,7 +1259344,7 @@ var ChannelSchema = exports_external.enum(["whatsapp", "discord", "telegram", "s
1259307
1259344
  var registerAssistantTools = (server, client) => {
1259308
1259345
  server.tool("message", "Send a message to a messaging channel (WhatsApp, Discord, Telegram, Slack). Use this to reply to messages or send proactive communications. For email, use create_gmail_draft instead.", {
1259309
1259346
  channel: ChannelSchema.describe("Target channel: whatsapp, discord, telegram, slack, or all"),
1259310
- to: exports_external.string().describe("Recipient (email address, phone number, or channel ID)"),
1259347
+ to: exports_external.optional(exports_external.string()).describe("Recipient identifier. Optional \u2014 if omitted, auto-resolves to the channel's primary user."),
1259311
1259348
  body: exports_external.string().describe("Message content"),
1259312
1259349
  subject: exports_external.optional(exports_external.string()).describe("Email subject (for email channel only)"),
1259313
1259350
  replyToMessageId: exports_external.optional(exports_external.string()).describe("Message ID to reply to (for threading)"),
@@ -1260510,7 +1260547,7 @@ mcpRoutes.all("/", async (c) => {
1260510
1260547
  });
1260511
1260548
  const server = new McpServer({
1260512
1260549
  name: "fulcrum",
1260513
- version: "2.16.1"
1260550
+ version: "2.16.2"
1260514
1260551
  });
1260515
1260552
  const client = new FulcrumClient(`http://localhost:${port}`);
1260516
1260553
  registerTools(server, client);
@@ -1261553,8 +1261590,8 @@ app23.post("/email/fetch", async (c) => {
1261553
1261590
  app23.post("/send", async (c) => {
1261554
1261591
  try {
1261555
1261592
  const body = await c.req.json();
1261556
- if (!body.channel || !body.to || !body.body) {
1261557
- return c.json({ error: "Missing required fields: channel, to, body" }, 400);
1261593
+ if (!body.channel || !body.body) {
1261594
+ return c.json({ error: "Missing required fields: channel, body" }, 400);
1261558
1261595
  }
1261559
1261596
  const result = await sendMessageToChannel(body.channel, body.to, body.body, {
1261560
1261597
  subject: body.subject,