Compare commits

...

2 Commits

Author SHA1 Message Date
Peter Steinberger
300d2c9339 fix: normalize agentId casing in cron/routing (#1591) (thanks @EnzeD) 2026-01-24 13:11:52 +00:00
Nicolas Zullo
cae7e3451f feat(templates): add emoji reactions guidance to AGENTS.md
## What
Add emoji reactions guidance to the default AGENTS.md template.

## Why  
Reactions are a natural, human-like way to acknowledge messages without cluttering chat. This should be default behavior.

## Testing
- Tested locally on Discord DM 
- Tested locally on Discord guild channel 

## AI-Assisted
This change was drafted with help from my Clawdbot instance (Clawd 🦞). 
We tested the behavior together before submitting.
2026-01-24 12:50:22 +00:00
7 changed files with 34 additions and 6 deletions

View File

@ -14,6 +14,7 @@ Docs: https://docs.clawd.bot
- Browser: add node-host proxy auto-routing for remote gateways (configurable per gateway/node). - Browser: add node-host proxy auto-routing for remote gateways (configurable per gateway/node).
- Heartbeat: add per-channel visibility controls (OK/alerts/indicator). (#1452) Thanks @dlauer. - Heartbeat: add per-channel visibility controls (OK/alerts/indicator). (#1452) Thanks @dlauer.
- Plugins: add optional llm-task JSON-only tool for workflows. (#1498) Thanks @vignesh07. - Plugins: add optional llm-task JSON-only tool for workflows. (#1498) Thanks @vignesh07.
- Docs: add emoji reaction guidance to AGENTS.md template. (#1591) Thanks @EnzeD.
- CLI: restart the gateway by default after `clawdbot update`; add `--no-restart` to skip it. - CLI: restart the gateway by default after `clawdbot update`; add `--no-restart` to skip it.
- CLI: add live auth probes to `clawdbot models status` for per-profile verification. - CLI: add live auth probes to `clawdbot models status` for per-profile verification.
- CLI: add `clawdbot system` for system events + heartbeat controls; remove standalone `wake`. - CLI: add `clawdbot system` for system events + heartbeat controls; remove standalone `wake`.
@ -27,6 +28,7 @@ Docs: https://docs.clawd.bot
### Fixes ### Fixes
- Sessions: accept non-UUID sessionIds for history/send/status while preserving agent scoping. (#1518) - Sessions: accept non-UUID sessionIds for history/send/status while preserving agent scoping. (#1518)
- Routing/Cron: normalize agentId casing for bindings and cron payloads. (#1591)
- Gateway: compare Linux process start time to avoid PID recycling lock loops; keep locks unless stale. (#1572) Thanks @steipete. - Gateway: compare Linux process start time to avoid PID recycling lock loops; keep locks unless stale. (#1572) Thanks @steipete.
- Messaging: mirror outbound sends into target session keys (threads + dmScope) and create session entries on send. (#1520) - Messaging: mirror outbound sends into target session keys (threads + dmScope) and create session entries on send. (#1520)
- Sessions: normalize session key casing to lowercase for consistent routing. - Sessions: normalize session key casing to lowercase for consistent routing.

View File

@ -92,6 +92,21 @@ In group chats where you receive every message, be **smart about when to contrib
Participate, don't dominate. Participate, don't dominate.
### 😊 React Like a Human!
On platforms that support reactions (Discord, Slack), use emoji reactions naturally:
**React when:**
- You appreciate something but don't need to reply (👍, ❤️, 🙌)
- Something made you laugh (😂, 💀)
- You find it interesting or thought-provoking (🤔, 💡)
- You want to acknowledge without interrupting the flow
- It's a simple yes/no or approval situation (✅, 👀)
**Why it matters:**
Reactions are lightweight social signals. Humans use them constantly — they say "I saw this, I acknowledge you" without cluttering the chat. You should too.
**Don't overdo it:** One reaction per message max. Pick the one that fits best.
## Tools ## Tools
Skills provide your tools. When you need one, check its `SKILL.md`. Keep local notes (camera names, SSH details, voice preferences) in `TOOLS.md`. Skills provide your tools. When you need one, check its `SKILL.md`. Keep local notes (camera names, SSH details, voice preferences) in `TOOLS.md`.

View File

@ -2,6 +2,7 @@ import type { Command } from "commander";
import type { CronJob } from "../../cron/types.js"; import type { CronJob } from "../../cron/types.js";
import { danger } from "../../globals.js"; import { danger } from "../../globals.js";
import { defaultRuntime } from "../../runtime.js"; import { defaultRuntime } from "../../runtime.js";
import { normalizeAgentId } from "../../routing/session-key.js";
import type { GatewayRpcOpts } from "../gateway-rpc.js"; import type { GatewayRpcOpts } from "../gateway-rpc.js";
import { addGatewayClientOptions, callGatewayFromCli } from "../gateway-rpc.js"; import { addGatewayClientOptions, callGatewayFromCli } from "../gateway-rpc.js";
import { parsePositiveIntOrUndefined } from "../program/helpers.js"; import { parsePositiveIntOrUndefined } from "../program/helpers.js";
@ -138,7 +139,9 @@ export function registerCronAddCommand(cron: Command) {
} }
const agentId = const agentId =
typeof opts.agent === "string" && opts.agent.trim() ? opts.agent.trim() : undefined; typeof opts.agent === "string" && opts.agent.trim()
? normalizeAgentId(opts.agent)
: undefined;
const payload = (() => { const payload = (() => {
const systemEvent = typeof opts.systemEvent === "string" ? opts.systemEvent.trim() : ""; const systemEvent = typeof opts.systemEvent === "string" ? opts.systemEvent.trim() : "";

View File

@ -1,6 +1,7 @@
import type { Command } from "commander"; import type { Command } from "commander";
import { danger } from "../../globals.js"; import { danger } from "../../globals.js";
import { defaultRuntime } from "../../runtime.js"; import { defaultRuntime } from "../../runtime.js";
import { normalizeAgentId } from "../../routing/session-key.js";
import { addGatewayClientOptions, callGatewayFromCli } from "../gateway-rpc.js"; import { addGatewayClientOptions, callGatewayFromCli } from "../gateway-rpc.js";
import { import {
getCronChannelOptions, getCronChannelOptions,
@ -90,7 +91,7 @@ export function registerCronEditCommand(cron: Command) {
throw new Error("Use --agent or --clear-agent, not both"); throw new Error("Use --agent or --clear-agent, not both");
} }
if (typeof opts.agent === "string" && opts.agent.trim()) { if (typeof opts.agent === "string" && opts.agent.trim()) {
patch.agentId = opts.agent.trim(); patch.agentId = normalizeAgentId(opts.agent);
} }
if (opts.clearAgent) { if (opts.clearAgent) {
patch.agentId = null; patch.agentId = null;

View File

@ -1,6 +1,7 @@
import { parseAbsoluteTimeMs } from "./parse.js"; import { parseAbsoluteTimeMs } from "./parse.js";
import { migrateLegacyCronPayload } from "./payload-migration.js"; import { migrateLegacyCronPayload } from "./payload-migration.js";
import type { CronJobCreate, CronJobPatch } from "./types.js"; import type { CronJobCreate, CronJobPatch } from "./types.js";
import { normalizeAgentId } from "../routing/session-key.js";
type UnknownRecord = Record<string, unknown>; type UnknownRecord = Record<string, unknown>;
@ -75,7 +76,7 @@ export function normalizeCronJobInput(
next.agentId = null; next.agentId = null;
} else if (typeof agentId === "string") { } else if (typeof agentId === "string") {
const trimmed = agentId.trim(); const trimmed = agentId.trim();
if (trimmed) next.agentId = trimmed; if (trimmed) next.agentId = normalizeAgentId(trimmed);
else delete next.agentId; else delete next.agentId;
} }
} }

View File

@ -95,7 +95,13 @@ function formatDiscordDeployErrorDetails(err: unknown): string {
try { try {
bodyText = JSON.stringify(rawBody); bodyText = JSON.stringify(rawBody);
} catch { } catch {
bodyText = String(rawBody); if (typeof rawBody === "string") {
bodyText = rawBody;
} else if (rawBody instanceof Error) {
bodyText = rawBody.message;
} else {
bodyText = "[unserializable]";
}
} }
if (bodyText) { if (bodyText) {
const maxLen = 800; const maxLen = 800;

View File

@ -96,9 +96,9 @@ function pickFirstExistingAgentId(cfg: ClawdbotConfig, agentId: string): string
if (!trimmed) return normalizeAgentId(resolveDefaultAgentId(cfg)); if (!trimmed) return normalizeAgentId(resolveDefaultAgentId(cfg));
const normalized = normalizeAgentId(trimmed); const normalized = normalizeAgentId(trimmed);
const agents = listAgents(cfg); const agents = listAgents(cfg);
if (agents.length === 0) return trimmed; if (agents.length === 0) return normalized;
const match = agents.find((agent) => normalizeAgentId(agent.id) === normalized); const match = agents.find((agent) => normalizeAgentId(agent.id) === normalized);
if (match?.id?.trim()) return match.id.trim(); if (match?.id?.trim()) return normalizeAgentId(match.id);
return normalizeAgentId(resolveDefaultAgentId(cfg)); return normalizeAgentId(resolveDefaultAgentId(cfg));
} }