@rubytech/taskmaster 1.11.2 → 1.12.0
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/dist/agents/skills/workspace.js +3 -0
- package/dist/agents/tools/brand-settings-tool.js +7 -2
- package/dist/agents/tools/channel-settings-tool.js +18 -4
- package/dist/agents/tools/public-chat-settings-tool.js +15 -5
- package/dist/agents/tools/system-status-tool.js +24 -6
- package/dist/build-info.json +3 -3
- package/dist/config/port-defaults.js +0 -8
- package/dist/config/zod-schema.js +6 -1
- package/dist/control-ui/assets/{index-s8s_YKvR.js → index-DpMaqt-b.js} +359 -347
- package/dist/control-ui/assets/index-DpMaqt-b.js.map +1 -0
- package/dist/control-ui/index.html +1 -1
- package/dist/gateway/control-ui.js +2 -2
- package/dist/gateway/public-chat/deliver-otp.js +49 -15
- package/dist/gateway/public-chat/deliver-sms.js +48 -13
- package/dist/gateway/public-chat-api.js +12 -7
- package/dist/gateway/server-methods/public-chat.js +11 -6
- package/package.json +1 -1
- package/skills/brevo/references/sms-credits.md +28 -20
- package/taskmaster-docs/USER-GUIDE.md +51 -8
- package/dist/control-ui/assets/index-s8s_YKvR.js.map +0 -1
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
<title>Taskmaster Control</title>
|
|
7
7
|
<meta name="color-scheme" content="dark light" />
|
|
8
8
|
<link rel="icon" type="image/png" href="./favicon.png" />
|
|
9
|
-
<script type="module" crossorigin src="./assets/index-
|
|
9
|
+
<script type="module" crossorigin src="./assets/index-DpMaqt-b.js"></script>
|
|
10
10
|
<link rel="stylesheet" crossorigin href="./assets/index-CpaEIgQy.css">
|
|
11
11
|
</head>
|
|
12
12
|
<body>
|
|
@@ -5,7 +5,7 @@ import { resolveAgentWorkspaceRoot } from "../agents/agent-scope.js";
|
|
|
5
5
|
import { buildAgentSummaries } from "../commands/agents.config.js";
|
|
6
6
|
import { DEFAULT_ASSISTANT_IDENTITY, resolveAssistantIdentity } from "./assistant-identity.js";
|
|
7
7
|
import { resolveAgentBoundAccountId } from "../routing/bindings.js";
|
|
8
|
-
import { detectOtpChannels } from "./public-chat/deliver-otp.js";
|
|
8
|
+
import { detectOtpChannels, normalizeVerifyMethods } from "./public-chat/deliver-otp.js";
|
|
9
9
|
import { resolvePublicAgentId } from "./public-chat/session.js";
|
|
10
10
|
import { findBrandLogo } from "./server-methods/brand.js";
|
|
11
11
|
import { buildControlUiAvatarUrl, CONTROL_UI_AVATAR_PREFIX, normalizeControlUiBasePath, resolveAssistantAvatarUrl, } from "./control-ui-shared.js";
|
|
@@ -570,7 +570,7 @@ export function handlePublicChatHttpRequest(req, res, opts) {
|
|
|
570
570
|
// Inject public-chat globals before </head>
|
|
571
571
|
const publicScript = `<script>` +
|
|
572
572
|
`window.__TASKMASTER_PUBLIC_CHAT__=true;` +
|
|
573
|
-
`window.__TASKMASTER_PUBLIC_CHAT_CONFIG__=${JSON.stringify({ accountId, auth: authMode, cookieTtlDays, otpChannels, verifyMethods: config.publicChat?.verifyMethods ?? ["
|
|
573
|
+
`window.__TASKMASTER_PUBLIC_CHAT_CONFIG__=${JSON.stringify({ accountId, auth: authMode, cookieTtlDays, otpChannels, verifyMethods: normalizeVerifyMethods(config.publicChat?.verifyMethods ?? ["whatsapp", "sms"]), greeting: config.publicChat?.greetings?.[accountId] || undefined })};` +
|
|
574
574
|
`</script>`;
|
|
575
575
|
const headClose = baseInjected.indexOf("</head>");
|
|
576
576
|
const withPublic = headClose !== -1
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Deliver OTP verification codes via WhatsApp
|
|
2
|
+
* Deliver OTP verification codes via WhatsApp, SMS (Brevo), or email (Brevo).
|
|
3
3
|
* Both SMS and email use the same Brevo API key — SMS requires prepaid credits.
|
|
4
|
+
*
|
|
5
|
+
* The admin controls which channels are enabled via `verifyMethods` config:
|
|
6
|
+
* "whatsapp", "sms", "email". The delivery logic only tries enabled channels.
|
|
4
7
|
*/
|
|
5
8
|
import { getActiveWebListener } from "../../web/active-listener.js";
|
|
6
9
|
import { sendMessageWhatsApp } from "../../web/outbound.js";
|
|
@@ -10,6 +13,27 @@ import { hasEmailApiKey, resolveEmailCredentials, sendEmail } from "./deliver-em
|
|
|
10
13
|
function isEmail(identifier) {
|
|
11
14
|
return identifier.includes("@");
|
|
12
15
|
}
|
|
16
|
+
/**
|
|
17
|
+
* Normalize verify methods — expand deprecated "phone" into ["whatsapp", "sms"].
|
|
18
|
+
* Returns a deduplicated array of canonical method names.
|
|
19
|
+
*/
|
|
20
|
+
export function normalizeVerifyMethods(methods) {
|
|
21
|
+
const result = new Set();
|
|
22
|
+
for (const m of methods) {
|
|
23
|
+
if (m === "phone") {
|
|
24
|
+
result.add("whatsapp");
|
|
25
|
+
result.add("sms");
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
result.add(m);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return [...result];
|
|
32
|
+
}
|
|
33
|
+
/** Check whether any phone-based verify method is enabled. */
|
|
34
|
+
export function hasPhoneMethod(methods) {
|
|
35
|
+
return methods.includes("whatsapp") || methods.includes("sms");
|
|
36
|
+
}
|
|
13
37
|
/**
|
|
14
38
|
* Detect which OTP delivery channels are currently available.
|
|
15
39
|
* Does not attempt delivery — used by the capabilities endpoint.
|
|
@@ -28,11 +52,12 @@ export function detectOtpChannels(whatsappAccountId) {
|
|
|
28
52
|
* Deliver a verification code to the given identifier.
|
|
29
53
|
*
|
|
30
54
|
* Email identifiers (containing @) are sent via Brevo email API.
|
|
31
|
-
* Phone identifiers
|
|
32
|
-
*
|
|
55
|
+
* Phone identifiers try WhatsApp then SMS, but only channels that
|
|
56
|
+
* are enabled in `enabledMethods` are attempted.
|
|
33
57
|
*/
|
|
34
|
-
export async function deliverOtp(identifier, code, accountId) {
|
|
58
|
+
export async function deliverOtp(identifier, code, accountId, enabledMethods) {
|
|
35
59
|
const message = `Your verification code is: ${code}`;
|
|
60
|
+
const methods = enabledMethods ?? ["whatsapp", "sms", "email"];
|
|
36
61
|
// Email identifiers — deliver via Brevo email API
|
|
37
62
|
if (isEmail(identifier)) {
|
|
38
63
|
const emailCreds = await resolveEmailCredentials();
|
|
@@ -42,24 +67,33 @@ export async function deliverOtp(identifier, code, accountId) {
|
|
|
42
67
|
await sendEmail(identifier, "Your verification code", message, emailCreds);
|
|
43
68
|
return { channel: "email" };
|
|
44
69
|
}
|
|
45
|
-
// Phone identifiers — WhatsApp
|
|
46
|
-
const
|
|
47
|
-
const
|
|
48
|
-
if (
|
|
70
|
+
// Phone identifiers — try enabled channels in order: WhatsApp, then SMS
|
|
71
|
+
const tryWhatsApp = methods.includes("whatsapp") && !!getActiveWebListener(accountId);
|
|
72
|
+
const trySms = methods.includes("sms");
|
|
73
|
+
if (tryWhatsApp) {
|
|
49
74
|
try {
|
|
50
75
|
await sendMessageWhatsApp(identifier, message, { verbose: false, accountId });
|
|
51
76
|
return { channel: "whatsapp" };
|
|
52
77
|
}
|
|
53
78
|
catch {
|
|
54
|
-
// WhatsApp failed — fall through to SMS
|
|
79
|
+
// WhatsApp failed — fall through to SMS if enabled
|
|
55
80
|
}
|
|
56
81
|
}
|
|
57
|
-
if (
|
|
58
|
-
await
|
|
59
|
-
|
|
82
|
+
if (trySms) {
|
|
83
|
+
const smsCreds = await resolveSmsCredentials();
|
|
84
|
+
if (smsCreds) {
|
|
85
|
+
await sendSms(identifier, message, smsCreds);
|
|
86
|
+
return { channel: "sms" };
|
|
87
|
+
}
|
|
60
88
|
}
|
|
61
|
-
|
|
62
|
-
|
|
89
|
+
// Build a specific error message based on what was tried
|
|
90
|
+
const tried = [];
|
|
91
|
+
if (methods.includes("whatsapp"))
|
|
92
|
+
tried.push("WhatsApp");
|
|
93
|
+
if (methods.includes("sms"))
|
|
94
|
+
tried.push("SMS");
|
|
95
|
+
if (tried.length === 0) {
|
|
96
|
+
throw new Error("No phone verification channel is enabled. Enable WhatsApp or SMS in Public Chat settings.");
|
|
63
97
|
}
|
|
64
|
-
throw new Error(
|
|
98
|
+
throw new Error(`Failed to send verification code via ${tried.join(" and ")}. Check that the channel is connected and configured.`);
|
|
65
99
|
}
|
|
@@ -1,18 +1,37 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Lightweight Brevo SMS sender — single HTTP POST, no SDK dependency.
|
|
3
3
|
*
|
|
4
|
-
* Uses the same Brevo API key as email delivery. The sender name
|
|
5
|
-
*
|
|
4
|
+
* Uses the same Brevo API key as email delivery. The SMS sender name comes
|
|
5
|
+
* from the same Brevo verified sender that email uses — matched by email
|
|
6
|
+
* address, so both channels present consistent branding.
|
|
6
7
|
* SMS requires prepaid credits in the Brevo account.
|
|
7
8
|
*/
|
|
8
9
|
import { loadConfig } from "../../config/config.js";
|
|
9
|
-
|
|
10
|
+
import { resolveEmailCredentials } from "./deliver-email.js";
|
|
11
|
+
/** Cached sender name (already truncated to ≤11 chars). */
|
|
10
12
|
let cachedSenderName = null;
|
|
11
13
|
/**
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
+
* Truncate a sender name to fit the GSM alphanumeric sender limit (11 chars).
|
|
15
|
+
* Tries to break at a word boundary to avoid ugly truncations.
|
|
14
16
|
*/
|
|
15
|
-
|
|
17
|
+
function truncateSender(name) {
|
|
18
|
+
if (name.length <= 11)
|
|
19
|
+
return name;
|
|
20
|
+
// Try the first word — often the business name
|
|
21
|
+
const firstWord = name.split(/\s/)[0];
|
|
22
|
+
if (firstWord.length <= 11)
|
|
23
|
+
return firstWord;
|
|
24
|
+
// Last resort: hard truncate
|
|
25
|
+
return name.slice(0, 11);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Resolve the SMS sender name from Brevo's verified senders.
|
|
29
|
+
*
|
|
30
|
+
* If an email address is provided (from config), finds the matching sender
|
|
31
|
+
* and uses its display name — this ensures SMS and email use the same identity.
|
|
32
|
+
* Falls back to the first active sender, then "Verify" as a safe default.
|
|
33
|
+
*/
|
|
34
|
+
async function fetchSenderName(apiKey, matchEmail) {
|
|
16
35
|
try {
|
|
17
36
|
const res = await fetch("https://api.brevo.com/v3/senders", {
|
|
18
37
|
headers: { "api-key": apiKey, Accept: "application/json" },
|
|
@@ -20,8 +39,17 @@ async function fetchSenderName(apiKey) {
|
|
|
20
39
|
if (!res.ok)
|
|
21
40
|
return "Verify";
|
|
22
41
|
const json = (await res.json());
|
|
23
|
-
const
|
|
24
|
-
|
|
42
|
+
const senders = json.senders ?? [];
|
|
43
|
+
// Match the sender whose email matches the one configured for email delivery
|
|
44
|
+
if (matchEmail) {
|
|
45
|
+
const match = senders.find((s) => s.email?.toLowerCase() === matchEmail.toLowerCase() && s.name);
|
|
46
|
+
if (match?.name)
|
|
47
|
+
return truncateSender(match.name);
|
|
48
|
+
}
|
|
49
|
+
// Fallback: first active sender with a name
|
|
50
|
+
const active = senders.find((s) => s.active && s.name);
|
|
51
|
+
const raw = active?.name ?? senders[0]?.name ?? "Verify";
|
|
52
|
+
return truncateSender(raw);
|
|
25
53
|
}
|
|
26
54
|
catch {
|
|
27
55
|
return "Verify";
|
|
@@ -38,8 +66,10 @@ export function hasSmsApiKey() {
|
|
|
38
66
|
/**
|
|
39
67
|
* Resolve SMS credentials from config.
|
|
40
68
|
*
|
|
41
|
-
* Uses the same Brevo API key as email. The sender name is resolved
|
|
42
|
-
*
|
|
69
|
+
* Uses the same Brevo API key as email. The sender name is resolved by asking
|
|
70
|
+
* the email delivery module which sender it uses, then finding that sender's
|
|
71
|
+
* display name in Brevo. This ensures both email and SMS present the same
|
|
72
|
+
* brand identity. The name is truncated to 11 characters (GSM limit).
|
|
43
73
|
* Returns null if no API key is configured.
|
|
44
74
|
*/
|
|
45
75
|
export async function resolveSmsCredentials() {
|
|
@@ -47,10 +77,15 @@ export async function resolveSmsCredentials() {
|
|
|
47
77
|
const apiKey = cfg.publicChat?.email?.apiKey;
|
|
48
78
|
if (!apiKey)
|
|
49
79
|
return null;
|
|
50
|
-
if (cachedSenderName &&
|
|
80
|
+
if (cachedSenderName &&
|
|
81
|
+
cachedSenderName.apiKey === apiKey &&
|
|
82
|
+
cachedSenderName.name.length <= 11) {
|
|
51
83
|
return { apiKey, sender: cachedSenderName.name };
|
|
52
84
|
}
|
|
53
|
-
|
|
85
|
+
// Resolve the email address that email delivery uses — ensures SMS matches
|
|
86
|
+
// the same Brevo sender regardless of whether `from` is set in config.
|
|
87
|
+
const emailCreds = await resolveEmailCredentials();
|
|
88
|
+
const name = await fetchSenderName(apiKey, emailCreds?.from);
|
|
54
89
|
cachedSenderName = { apiKey, name };
|
|
55
90
|
return { apiKey, sender: name };
|
|
56
91
|
}
|
|
@@ -74,7 +109,7 @@ export async function sendSms(to, body, creds) {
|
|
|
74
109
|
});
|
|
75
110
|
if (!res.ok) {
|
|
76
111
|
const text = await res.text().catch(() => "");
|
|
77
|
-
throw new Error(`Brevo SMS failed (${res.status}): ${text}`);
|
|
112
|
+
throw new Error(`Brevo SMS failed (${res.status}): ${text} [sender=${JSON.stringify(creds.sender)}, recipient=${to}]`);
|
|
78
113
|
}
|
|
79
114
|
const json = (await res.json());
|
|
80
115
|
return { id: String(json.messageId ?? "unknown") };
|
|
@@ -36,7 +36,7 @@ import { createInternalHookEvent, triggerInternalHook } from "../hooks/internal-
|
|
|
36
36
|
import { emitAgentEvent, onAgentEvent } from "../infra/agent-events.js";
|
|
37
37
|
import { INTERNAL_MESSAGE_CHANNEL } from "../utils/message-channel.js";
|
|
38
38
|
import { requestOtp, verifyOtp } from "./public-chat/otp.js";
|
|
39
|
-
import { deliverOtp, detectOtpChannels } from "./public-chat/deliver-otp.js";
|
|
39
|
+
import { deliverOtp, detectOtpChannels, hasPhoneMethod, normalizeVerifyMethods, } from "./public-chat/deliver-otp.js";
|
|
40
40
|
import { buildPublicSessionKey, resolvePublicAgentId } from "./public-chat/session.js";
|
|
41
41
|
import { loadSessionEntry, readSessionMessages } from "./session-utils.js";
|
|
42
42
|
import { extractFileAttachments, sanitizeMediaForChat, stripEnvelopeFromMessages, } from "./chat-sanitize.js";
|
|
@@ -228,10 +228,10 @@ async function handleOtpRequest(req, res, accountId, cfg, maxBodyBytes) {
|
|
|
228
228
|
: typeof payload.phone === "string"
|
|
229
229
|
? payload.phone.trim()
|
|
230
230
|
: "";
|
|
231
|
-
const
|
|
232
|
-
const verifyMethods = cfg.publicChat?.verifyMethods ?? ["
|
|
231
|
+
const isEmailId = rawIdentifier.includes("@");
|
|
232
|
+
const verifyMethods = normalizeVerifyMethods(cfg.publicChat?.verifyMethods ?? ["whatsapp", "sms"]);
|
|
233
233
|
let identifier;
|
|
234
|
-
if (
|
|
234
|
+
if (isEmailId) {
|
|
235
235
|
if (!verifyMethods.includes("email")) {
|
|
236
236
|
sendForbidden(res, "email verification is not enabled for this account");
|
|
237
237
|
return;
|
|
@@ -243,7 +243,7 @@ async function handleOtpRequest(req, res, accountId, cfg, maxBodyBytes) {
|
|
|
243
243
|
}
|
|
244
244
|
}
|
|
245
245
|
else {
|
|
246
|
-
if (!verifyMethods
|
|
246
|
+
if (!hasPhoneMethod(verifyMethods)) {
|
|
247
247
|
sendForbidden(res, "phone verification is not enabled for this account");
|
|
248
248
|
return;
|
|
249
249
|
}
|
|
@@ -265,8 +265,13 @@ async function handleOtpRequest(req, res, accountId, cfg, maxBodyBytes) {
|
|
|
265
265
|
// OTP code is sent from the correct number (not the first active account).
|
|
266
266
|
const agentId = resolvePublicAgentId(cfg, accountId);
|
|
267
267
|
const whatsappAccountId = resolveAgentBoundAccountId(cfg, agentId, "whatsapp") ?? undefined;
|
|
268
|
+
// Narrow delivery to visitor's preferred channel if specified and admin-enabled
|
|
269
|
+
const preferredChannel = typeof payload.channel === "string" ? payload.channel : undefined;
|
|
270
|
+
const deliveryMethods = preferredChannel && verifyMethods.includes(preferredChannel)
|
|
271
|
+
? [preferredChannel]
|
|
272
|
+
: verifyMethods;
|
|
268
273
|
try {
|
|
269
|
-
const delivery = await deliverOtp(identifier, result.code, whatsappAccountId);
|
|
274
|
+
const delivery = await deliverOtp(identifier, result.code, whatsappAccountId, deliveryMethods);
|
|
270
275
|
sendJson(res, 200, { ok: true, channel: delivery.channel });
|
|
271
276
|
}
|
|
272
277
|
catch (err) {
|
|
@@ -751,7 +756,7 @@ async function handleCapabilities(req, res, accountId, cfg) {
|
|
|
751
756
|
return;
|
|
752
757
|
}
|
|
753
758
|
const authMode = cfg.publicChat?.auth ?? "anonymous";
|
|
754
|
-
const verifyMethods = cfg.publicChat?.verifyMethods ?? ["
|
|
759
|
+
const verifyMethods = normalizeVerifyMethods(cfg.publicChat?.verifyMethods ?? ["whatsapp", "sms"]);
|
|
755
760
|
const agentId = resolvePublicAgentId(cfg, accountId);
|
|
756
761
|
const whatsappAccountId = resolveAgentBoundAccountId(cfg, agentId, "whatsapp") ?? undefined;
|
|
757
762
|
const channels = detectOtpChannels(whatsappAccountId);
|
|
@@ -6,7 +6,7 @@ import { resolveAgentBoundAccountId } from "../../routing/bindings.js";
|
|
|
6
6
|
import { generateGreeting } from "../../suggestions/greeting.js";
|
|
7
7
|
import { ErrorCodes, errorShape } from "../protocol/index.js";
|
|
8
8
|
import { requestOtp, verifyOtp } from "../public-chat/otp.js";
|
|
9
|
-
import { deliverOtp } from "../public-chat/deliver-otp.js";
|
|
9
|
+
import { deliverOtp, hasPhoneMethod, normalizeVerifyMethods } from "../public-chat/deliver-otp.js";
|
|
10
10
|
import { buildPublicSessionKey, resolvePublicAgentId } from "../public-chat/session.js";
|
|
11
11
|
/** Strip spaces, dashes, and parentheses from a phone number. */
|
|
12
12
|
function normalizePhone(raw) {
|
|
@@ -46,10 +46,10 @@ export const publicChatHandlers = {
|
|
|
46
46
|
respond(false, undefined, errorShape(ErrorCodes.INVALID_REQUEST, "public chat disabled"));
|
|
47
47
|
return;
|
|
48
48
|
}
|
|
49
|
-
const
|
|
50
|
-
const verifyMethods = cfg.publicChat?.verifyMethods ?? ["
|
|
49
|
+
const isEmailId = rawIdentifier.includes("@");
|
|
50
|
+
const verifyMethods = normalizeVerifyMethods(cfg.publicChat?.verifyMethods ?? ["whatsapp", "sms"]);
|
|
51
51
|
let identifier;
|
|
52
|
-
if (
|
|
52
|
+
if (isEmailId) {
|
|
53
53
|
if (!verifyMethods.includes("email")) {
|
|
54
54
|
respond(false, undefined, errorShape(ErrorCodes.INVALID_REQUEST, "email verification is not enabled"));
|
|
55
55
|
return;
|
|
@@ -61,7 +61,7 @@ export const publicChatHandlers = {
|
|
|
61
61
|
}
|
|
62
62
|
}
|
|
63
63
|
else {
|
|
64
|
-
if (!verifyMethods
|
|
64
|
+
if (!hasPhoneMethod(verifyMethods)) {
|
|
65
65
|
respond(false, undefined, errorShape(ErrorCodes.INVALID_REQUEST, "phone verification is not enabled"));
|
|
66
66
|
return;
|
|
67
67
|
}
|
|
@@ -82,8 +82,13 @@ export const publicChatHandlers = {
|
|
|
82
82
|
const whatsappAccountId = agentId
|
|
83
83
|
? (resolveAgentBoundAccountId(cfg, agentId, "whatsapp") ?? undefined)
|
|
84
84
|
: undefined;
|
|
85
|
+
// Narrow delivery to visitor's preferred channel if specified and admin-enabled
|
|
86
|
+
const preferredChannel = typeof params.channel === "string" ? params.channel : undefined;
|
|
87
|
+
const deliveryMethods = preferredChannel && verifyMethods.includes(preferredChannel)
|
|
88
|
+
? [preferredChannel]
|
|
89
|
+
: verifyMethods;
|
|
85
90
|
try {
|
|
86
|
-
const delivery = await deliverOtp(identifier, result.code, whatsappAccountId);
|
|
91
|
+
const delivery = await deliverOtp(identifier, result.code, whatsappAccountId, deliveryMethods);
|
|
87
92
|
respond(true, { ok: true, channel: delivery.channel });
|
|
88
93
|
}
|
|
89
94
|
catch (err) {
|
package/package.json
CHANGED
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
Walk the user through purchasing SMS credits in Brevo. SMS verification uses the same Brevo API key as email — no additional configuration in Taskmaster. The user just needs credits in their Brevo account.
|
|
4
4
|
|
|
5
|
-
**Important:** SMS credits are prepaid and never expire.
|
|
5
|
+
**Important:** SMS credits are prepaid and never expire. The cost depends on the destination country and number of messages.
|
|
6
|
+
|
|
7
|
+
**SMS sender name:** The name that appears on the SMS (e.g. "Taskmaster") is taken from the same Brevo verified sender used for email — matched by email address. If you have multiple senders in Brevo, the one whose email matches the configured sender is used. The name is truncated to 11 characters (GSM limit) — if longer, the first word is used (e.g. "Rubytech LLC" becomes "Rubytech"). To control the SMS sender name, edit the sender's display name in Brevo under **Senders, Domains & Dedicated IPs → Senders**.
|
|
6
8
|
|
|
7
9
|
---
|
|
8
10
|
|
|
@@ -21,38 +23,43 @@ Walk the user through purchasing SMS credits in Brevo. SMS verification uses the
|
|
|
21
23
|
|
|
22
24
|
## Step 2: Purchase SMS credits
|
|
23
25
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
>
|
|
27
|
-
>
|
|
28
|
-
>
|
|
29
|
-
>
|
|
30
|
-
>
|
|
31
|
-
> -
|
|
32
|
-
>
|
|
26
|
+
**Navigation: Dashboard home → lightning icon (top-right toolbar) → "Campaign usage and plan" popup → SMS section → "Get more credits".**
|
|
27
|
+
|
|
28
|
+
> "1. Go to **app.brevo.com** and log in — you should see the main dashboard ('Hello [name]')
|
|
29
|
+
> 2. In the **top-right toolbar**, click the **lightning bolt icon** (⚡) — it's to the left of the help (?) and gear (⚙️) icons
|
|
30
|
+
> 3. A **'Campaign usage and plan'** popup appears showing your plan, email credits, and SMS credits
|
|
31
|
+
> 4. Under the **SMS** section (shows '0 credits left' if you haven't bought any), click **'Get more credits'**
|
|
32
|
+
> 5. On the **'Add messages credits'** page:
|
|
33
|
+
> - **Type of message** — select **SMS**
|
|
34
|
+
> - **Number of messages** — enter **100** (a good starting amount for verification codes)
|
|
35
|
+
> - **Message destination** — select your country (e.g. **United Kingdom**)
|
|
36
|
+
> - The **Purchase summary** on the right shows the total cost
|
|
37
|
+
> 6. Click **'Proceed to checkout'** and complete the purchase
|
|
33
38
|
>
|
|
34
39
|
> Let me know when that's done."
|
|
35
40
|
|
|
41
|
+
If the user can't find the lightning icon:
|
|
42
|
+
|
|
43
|
+
> "In the top-right toolbar of the Brevo dashboard, there are several small icons. The lightning bolt (⚡) is the first one on the left, before the help (?), gear (⚙️), bell (🔔), and chart icons. Click that — it opens a popup showing your plan and credits."
|
|
44
|
+
|
|
36
45
|
Wait for the user to confirm.
|
|
37
46
|
|
|
38
47
|
## Step 3: Confirm
|
|
39
48
|
|
|
40
|
-
> "SMS verification
|
|
49
|
+
> "SMS credits are ready. To enable SMS verification, go to the **Setup** page → **Public Chat** settings → check the **SMS** checkbox under verification methods. You can enable SMS alongside WhatsApp, or on its own. When a visitor chooses SMS on the public chat page, the code is sent by text message. No restart needed — it uses your existing Brevo API key."
|
|
41
50
|
|
|
42
51
|
---
|
|
43
52
|
|
|
44
53
|
## Pricing reference
|
|
45
54
|
|
|
46
|
-
|
|
55
|
+
Pricing depends on the destination country and volume. The exact cost is shown on the checkout page when you select a country and message count. As a rough guide:
|
|
47
56
|
|
|
48
|
-
|
|
|
49
|
-
|
|
57
|
+
| Destination | 100 messages |
|
|
58
|
+
|-------------|-------------|
|
|
59
|
+
| UK | ~£2.82 |
|
|
50
60
|
| US / Canada | ~$1.09 |
|
|
51
|
-
| UK | ~$3.45 |
|
|
52
|
-
| Australia | ~$5.50 |
|
|
53
|
-
| EU (varies) | ~$3.00–$7.00 |
|
|
54
61
|
|
|
55
|
-
|
|
62
|
+
Credits are called "message credits" and can be used for SMS. On the free plan, WhatsApp messages via Brevo require a Professional plan upgrade — but we don't use Brevo for WhatsApp, so this doesn't apply.
|
|
56
63
|
|
|
57
64
|
---
|
|
58
65
|
|
|
@@ -61,8 +68,9 @@ Exact pricing is shown during purchase in the Brevo dashboard.
|
|
|
61
68
|
| Question | Answer |
|
|
62
69
|
|----------|--------|
|
|
63
70
|
| "Do credits expire?" | No — prepaid SMS credits never expire. |
|
|
64
|
-
| "How many credits per SMS?" |
|
|
71
|
+
| "How many credits per SMS?" | Depends on the destination country. The purchase page shows the exact conversion (e.g. 316.5 credits = 100 UK messages). |
|
|
65
72
|
| "What phone number do SMS come from?" | Brevo assigns a sender automatically based on the destination country. You don't need to buy or manage a phone number. |
|
|
66
|
-
| "What name appears on the SMS?" | The
|
|
73
|
+
| "What name appears on the SMS?" | The display name from the same Brevo verified sender used for email (matched by email address). Must be 11 characters or fewer — if longer, the first word is used. Edit sender names under **Senders, Domains & Dedicated IPs → Senders** in Brevo. |
|
|
67
74
|
| "Do I need to change anything in Taskmaster?" | No. If your Brevo API key is already configured, SMS works automatically when you have credits. |
|
|
68
75
|
| "Can I use SMS without email?" | The Brevo API key is configured through the email setup, but once configured it serves both email and SMS. You need at least one verified sender. |
|
|
76
|
+
| "Where do I check my remaining credits?" | Click the lightning bolt icon (⚡) in the top-right toolbar. The popup shows your SMS credits remaining. |
|
|
@@ -557,6 +557,31 @@ Your assistant can manage who has admin access — the same as using the Admins
|
|
|
557
557
|
- "Who are my current admins?"
|
|
558
558
|
- "Remove the old number ending in 789 from admins"
|
|
559
559
|
|
|
560
|
+
### System Administration
|
|
561
|
+
|
|
562
|
+
Your assistant can check and manage many system settings directly through chat — the same things you'd normally check in the Setup page on the control panel. Think of it as a shortcut: instead of opening the control panel and navigating to the right setting, just ask.
|
|
563
|
+
|
|
564
|
+
| Capability | Description |
|
|
565
|
+
|------------|-------------|
|
|
566
|
+
| System status | Check gateway health, WhatsApp/iMessage connections, Claude authentication, license, software version |
|
|
567
|
+
| Usage and cost | See how many tokens you've used and what it's costing, broken down by provider |
|
|
568
|
+
| Channel settings | Change the AI model, thinking level, DM policy, or group chat policy for your WhatsApp accounts |
|
|
569
|
+
| Branding | Update your accent or background colour |
|
|
570
|
+
| Public chat | Enable or disable the public chat widget, change the greeting, set visitor verification |
|
|
571
|
+
| Skills | List, create, delete, or toggle skills |
|
|
572
|
+
| Logs | Review system and session logs for troubleshooting |
|
|
573
|
+
|
|
574
|
+
**Try asking:**
|
|
575
|
+
- "How's the system doing?"
|
|
576
|
+
- "What's my usage this month?"
|
|
577
|
+
- "Is WhatsApp connected?"
|
|
578
|
+
- "Switch WhatsApp to a cheaper model"
|
|
579
|
+
- "Change the accent colour to blue"
|
|
580
|
+
- "Enable public chat"
|
|
581
|
+
- "Show me the recent logs"
|
|
582
|
+
|
|
583
|
+
> **Note:** Some things still need the control panel: scanning WhatsApp QR codes, uploading logos, WiFi setup, Tailscale remote access, and PIN management. Your assistant will tell you when to open the control panel and where to go.
|
|
584
|
+
|
|
560
585
|
### Web Browsing
|
|
561
586
|
|
|
562
587
|
Your assistant can open and interact with websites in a built-in browser — filling in forms, clicking buttons, and navigating pages. You can watch what it's doing on the **Browser** page in real time, and take over if it needs help (for example, to complete a CAPTCHA or log in to a site).
|
|
@@ -1001,6 +1026,21 @@ If the Pi checks pass but the control panel still doesn't load from another devi
|
|
|
1001
1026
|
2. **Check you're on the same subnet** — mDNS only works between devices on the same network subnet. It won't work across separate subnets, VLANs, or guest networks. A common cause: the Pi is on Ethernet while your device is on WiFi, and your router assigns them to different subnets. Check by comparing the first three groups of your IP addresses (e.g. if the Pi is `192.168.88.4` and your laptop is `192.168.10.172`, they're on different subnets).
|
|
1002
1027
|
3. **Contact support** with: what you changed (port/hostname), the output of `hostname`, `grep 127.0.1.1 /etc/hosts`, and `taskmaster daemon status`.
|
|
1003
1028
|
|
|
1029
|
+
#### Hostname changed to `taskmaster-2.local` (or similar)
|
|
1030
|
+
|
|
1031
|
+
If your control panel URL suddenly stops working and you notice the hostname has a `-2` (or `-3`) suffix, the Pi detected a name conflict on the network and renamed itself.
|
|
1032
|
+
|
|
1033
|
+
**When this happens:** If you unplug the Ethernet cable while the Pi is already running on WiFi, the Pi briefly had two network connections active at the same time. Its hostname detection saw its own name coming from the Ethernet side and treated it as a conflict, adding a `-2` suffix.
|
|
1034
|
+
|
|
1035
|
+
**Fix:**
|
|
1036
|
+
|
|
1037
|
+
1. Power the Pi off completely (not just restart — fully power off)
|
|
1038
|
+
2. Wait at least 2 minutes (this lets the mDNS name cache clear on all devices on the network)
|
|
1039
|
+
3. Power the Pi back on
|
|
1040
|
+
4. Access the control panel using your normal URL
|
|
1041
|
+
|
|
1042
|
+
**To avoid this in future:** Connect via WiFi first, then remove the Ethernet cable — don't unplug Ethernet while the Pi is running with both cables connected.
|
|
1043
|
+
|
|
1004
1044
|
### Static IP Address
|
|
1005
1045
|
|
|
1006
1046
|
You don't need to assign a static IP address — Taskmaster uses a `.local` hostname that resolves automatically via mDNS, even if the IP changes after a reboot or DHCP renewal. You'll always find your control panel at the same `.local` address.
|
|
@@ -1467,16 +1507,19 @@ By default, anyone who opens the public chat can start a conversation immediatel
|
|
|
1467
1507
|
1. Go to the **Setup** page
|
|
1468
1508
|
2. Find the **Public Chat** status card
|
|
1469
1509
|
3. Click the settings icon to open the verification settings modal
|
|
1470
|
-
4. Choose
|
|
1510
|
+
4. Choose an auth mode: **Anonymous** (no verification), **Verified** (must verify before chatting), or **Visitor chooses** (can chat immediately or verify for cross-device continuity)
|
|
1511
|
+
5. Select which verification channels to enable — three independent checkboxes:
|
|
1512
|
+
|
|
1513
|
+
| Channel | How it works | What you need |
|
|
1514
|
+
|---------|-------------|---------------|
|
|
1515
|
+
| **WhatsApp** | Verification code sent via WhatsApp | A connected WhatsApp account |
|
|
1516
|
+
| **SMS** | Verification code sent by text message | Same [Brevo](https://brevo.com) API key as email, plus prepaid SMS credits in your Brevo account |
|
|
1517
|
+
| **Email** | Verification code sent by email | A [Brevo](https://brevo.com) API key and a verified sender (free, no credit card) |
|
|
1471
1518
|
|
|
1472
|
-
|
|
1473
|
-
|--------|-------------|---------------|
|
|
1474
|
-
| **Anonymous** | No verification — visitors chat immediately | Nothing (default) |
|
|
1475
|
-
| **Email OTP** | Visitors enter their email, receive a one-time code | A [Brevo](https://brevo.com) API key and a verified sender (free, no credit card). The sender address is configured in Brevo, not in Taskmaster. |
|
|
1476
|
-
| **SMS OTP** | Visitors enter their phone number, receive a one-time code | Same [Brevo](https://brevo.com) API key as email, plus prepaid SMS credits in your Brevo account |
|
|
1519
|
+
When both WhatsApp and SMS are enabled, phone verification tries WhatsApp first and falls back to SMS. To use SMS only, uncheck WhatsApp.
|
|
1477
1520
|
|
|
1478
|
-
|
|
1479
|
-
|
|
1521
|
+
6. Enter your provider credentials — add the Brevo API key in the **API Keys** section on the Setup page. The same key serves both email and SMS verification. For SMS, purchase SMS credits in your Brevo dashboard.
|
|
1522
|
+
7. Settings auto-save when you change a checkbox or leave a field
|
|
1480
1523
|
|
|
1481
1524
|
Once enabled, visitors must authenticate before they can send their first message. Verified contact details are stored and available to the agent via the `verify_contact` tool, so your assistant knows who it's talking to.
|
|
1482
1525
|
|