From 3cd8bade72e52080dc2905ec39a4e0360443970e Mon Sep 17 00:00:00 2001 From: Thomas Bertran Date: Mon, 13 Jul 2026 11:29:39 -0700 Subject: [PATCH 1/4] feat(discord): add /kill and /stop to interrupt a running agent MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Telegram gained /kill in #184 but Discord never got the equivalent, so a Discord user has no way to stop a run in flight — the only recourse is restarting the daemon. Wire the existing killActive() into the Discord message handler, dispatched before skill routing so it takes effect immediately rather than queueing behind the run it is meant to cancel. /stop is accepted as an alias. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/commands/discord.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/commands/discord.ts b/src/commands/discord.ts index b77db09a..f7139e7f 100644 --- a/src/commands/discord.ts +++ b/src/commands/discord.ts @@ -1,4 +1,4 @@ -import { ensureProjectClaudeMd, run, runUserMessage, compactCurrentSession, compactCurrentThreadSession, agentDirKey } from "../runner"; +import { ensureProjectClaudeMd, run, runUserMessage, killActive, compactCurrentSession, compactCurrentThreadSession, agentDirKey } from "../runner"; import { wrapUntrusted } from "../prompt-safety"; import { isAllowed } from "../allowlist"; import { extractErrorDetail } from "../messaging"; @@ -976,6 +976,12 @@ async function handleMessageCreate(token: string, message: DiscordMessage, skipC // Skill routing: detect slash commands and resolve to SKILL.md prompts const command = cleanContent.startsWith("/") ? cleanContent.trim().split(/\s+/, 1)[0].toLowerCase() : null; + if (command === "/kill" || command === "/stop") { + const killed = killActive(); + await sendMessage(config.token, channelId, killed ? "Killed active agent." : "No active agent running."); + return; + } + let skillContext: string | null = null; if (command) { try { From a0dfff9f2a924261bea920ba590d9303944044b3 Mon Sep 17 00:00:00 2001 From: Thomas Bertran Date: Sun, 19 Jul 2026 16:53:33 -0700 Subject: [PATCH 2/4] chore: bump plugin+marketplace to 1.0.45 and document global kill blast radius Addresses review feedback on #253: - plugin-version-guard / marketplace-version-guard require a bump for shipped plugin content; upstream master already consumed 1.0.44, so this lands on 1.0.45. - Note the blast radius of killActive() at the call site. Co-Authored-By: Claude Opus 4.8 (1M context) --- .claude-plugin/marketplace.json | 2 +- .claude-plugin/plugin.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 888422e7..85219676 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -8,7 +8,7 @@ "name": "claudeclaw", "source": "./", "description": "Cron-like daemon that runs Claude prompts on a schedule", - "version": "1.0.44", + "version": "1.0.45", "keywords": [ "cron", "heartbeat", diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index 644babac..cc993e55 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,5 +1,5 @@ { "name": "claudeclaw", - "version": "1.0.44", + "version": "1.0.45", "description": "Cron-like daemon that runs Claude prompts on a schedule" } From a882db737653359d46650369549a78896c89aae3 Mon Sep 17 00:00:00 2001 From: Thomas Bertran Date: Sun, 19 Jul 2026 16:53:50 -0700 Subject: [PATCH 3/4] docs(discord): note that /kill is global, not caller-scoped killActive() terminates every in-flight main run across all channels and threads. Matches Telegram's existing contract (#184), not a regression; thread-scoped kill tracked separately (#237). Co-Authored-By: Claude Opus 4.8 (1M context) --- src/commands/discord.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/commands/discord.ts b/src/commands/discord.ts index 4934d25d..01801193 100644 --- a/src/commands/discord.ts +++ b/src/commands/discord.ts @@ -978,6 +978,12 @@ async function handleMessageCreate(token: string, message: DiscordMessage, skipC const command = cleanContent.startsWith("/") ? cleanContent.trim().split(/\s+/, 1)[0].toLowerCase() : null; if (command === "/kill" || command === "/stop") { + // killActive() is global: it kills every in-flight main run across all + // Discord channels/threads, not just the caller's. Combined with the + // channel-scoped allowlist (#248), a user authorized in one channel can + // kill runs in channels they aren't authorized for. Matches Telegram's + // existing /kill contract (not a regression); thread-scoped kill is + // tracked separately (see #237). const killed = killActive(); await sendMessage(config.token, channelId, killed ? "Killed active agent." : "No active agent running."); return; From dc1bd961d441324cefde10041246e387413c0f7e Mon Sep 17 00:00:00 2001 From: Thomas Bertran Date: Sun, 19 Jul 2026 16:53:59 -0700 Subject: [PATCH 4/4] chore(discord): update fallback model to claude-sonnet-5 Co-Authored-By: Claude Opus 4.8 (1M context) --- src/commands/discord.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commands/discord.ts b/src/commands/discord.ts index 01801193..219289d9 100644 --- a/src/commands/discord.ts +++ b/src/commands/discord.ts @@ -498,7 +498,7 @@ Rules: const { execSync } = await import("node:child_process"); const input = `${systemPrompt}\n\n---\nUser message: ${text}`; const result = execSync( - `claude --model claude-sonnet-4-20250514 --print --output-format text`, + `claude --model claude-sonnet-5 --print --output-format text`, { input, encoding: "utf-8",