@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 +4 -4
- package/package.json +1 -1
- package/server/index.js +61 -24
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
|
|
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.
|
|
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.
|
|
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.
|
|
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
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 (
|
|
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}",
|
|
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
|
|
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
|
|
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(
|
|
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:
|
|
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(
|
|
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:
|
|
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(
|
|
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:
|
|
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(
|
|
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:
|
|
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
|
|
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.
|
|
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.
|
|
1261557
|
-
return c.json({ error: "Missing required fields: channel,
|
|
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,
|