forge-remote 0.1.15 → 0.1.16

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "forge-remote",
3
- "version": "0.1.15",
3
+ "version": "0.1.16",
4
4
  "description": "Desktop relay for Forge Remote — monitor and control Claude Code sessions from your phone",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",
@@ -924,9 +924,14 @@ async function runClaudeProcess(sessionId, prompt) {
924
924
 
925
925
  // If this session was triggered by a webhook with a reply URL,
926
926
  // post Claude's last response back to the source (e.g., Slack).
927
+ if (sess?.webhookMeta) {
928
+ log.info(
929
+ `Webhook meta for ${sessionId}: replyUrl=${sess.webhookMeta.replyUrl || "none"}, lastText=${(sess.lastAssistantText || "").slice(0, 50) || "none"}`,
930
+ );
931
+ }
927
932
  if (sess?.webhookMeta?.replyUrl && sess.lastAssistantText) {
928
933
  postToSlack(sess.webhookMeta.replyUrl, sess.lastAssistantText).catch(
929
- () => {},
934
+ (err) => log.error(`Slack reply error: ${err.message}`),
930
935
  );
931
936
  }
932
937
 
@@ -321,6 +321,36 @@ async function handleWebhookPost(req, res, webhookId, sourceIp) {
321
321
  return sendJson(res, 400, { error: "invalid JSON body" });
322
322
  }
323
323
 
324
+ // 7a. Slack event_callback handling — extract message from event envelope
325
+ // and ignore bot messages to prevent infinite reply loops.
326
+ if (source === "slack" && payload.type === "event_callback") {
327
+ const event = payload.event;
328
+ if (!event) {
329
+ return sendJson(res, 200, { status: "ignored", reason: "no event" });
330
+ }
331
+
332
+ // Ignore bot messages (including our own replies) to prevent loops.
333
+ if (event.bot_id || event.subtype === "bot_message") {
334
+ return sendJson(res, 200, { status: "ignored", reason: "bot message" });
335
+ }
336
+
337
+ // Ignore message edits/deletes — only handle new messages.
338
+ if (event.subtype && event.subtype !== "file_share") {
339
+ return sendJson(res, 200, {
340
+ status: "ignored",
341
+ reason: `subtype: ${event.subtype}`,
342
+ });
343
+ }
344
+
345
+ // Flatten the Slack event into the payload for template rendering.
346
+ // This lets templates use {{text}}, {{user}}, {{channel}} directly.
347
+ payload = { ...payload, ...event };
348
+
349
+ log.info(
350
+ `Slack message from user ${event.user || "unknown"} in channel ${event.channel || "unknown"}: "${(event.text || "").slice(0, 80)}"`,
351
+ );
352
+ }
353
+
324
354
  const prompt = renderTemplate(config.promptTemplate || "", payload);
325
355
  const projectPath = config.projectPath || process.cwd();
326
356
  const model = config.model || "sonnet";