@rubytech/taskmaster 1.11.0 → 1.11.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/dist/agents/taskmaster-tools.js +14 -0
- package/dist/agents/tool-policy.js +18 -0
- package/dist/agents/tools/brand-settings-tool.js +101 -0
- package/dist/agents/tools/channel-settings-tool.js +116 -0
- package/dist/agents/tools/logs-read-tool.js +67 -0
- package/dist/agents/tools/public-chat-settings-tool.js +128 -0
- package/dist/agents/tools/skill-manage-tool.js +105 -0
- package/dist/agents/tools/system-status-tool.js +64 -0
- package/dist/agents/tools/usage-report-tool.js +46 -0
- package/dist/build-info.json +3 -3
- package/dist/control-ui/assets/{index-YAjVyXqJ.js → index-s8s_YKvR.js} +494 -526
- package/dist/control-ui/assets/index-s8s_YKvR.js.map +1 -0
- package/dist/control-ui/index.html +1 -1
- package/dist/gateway/public-chat/deliver-otp.js +8 -6
- package/dist/gateway/public-chat/deliver-sms.js +61 -24
- package/package.json +1 -1
- package/skills/brevo/SKILL.md +9 -8
- package/skills/brevo/references/browser-setup.md +81 -23
- package/skills/brevo/references/sms-credits.md +68 -0
- package/skills/system-admin/SKILL.md +68 -0
- package/skills/twilio/SKILL.md +14 -11
- package/skills/twilio/references/browser-setup.md +15 -24
- package/taskmaster-docs/USER-GUIDE.md +3 -3
- package/dist/control-ui/assets/index-YAjVyXqJ.js.map +0 -1
|
@@ -32,8 +32,15 @@ import { createRelayMessageTool } from "./tools/relay-message-tool.js";
|
|
|
32
32
|
import { createSkillDraftSaveTool } from "./tools/skill-draft-save-tool.js";
|
|
33
33
|
import { createSkillReadTool } from "./tools/skill-read-tool.js";
|
|
34
34
|
import { createApiKeysTool } from "./tools/apikeys-tool.js";
|
|
35
|
+
import { createBrandSettingsTool } from "./tools/brand-settings-tool.js";
|
|
36
|
+
import { createChannelSettingsTool } from "./tools/channel-settings-tool.js";
|
|
35
37
|
import { createImageGenerateTool } from "./tools/image-generate-tool.js";
|
|
38
|
+
import { createLogsReadTool } from "./tools/logs-read-tool.js";
|
|
39
|
+
import { createPublicChatSettingsTool } from "./tools/public-chat-settings-tool.js";
|
|
40
|
+
import { createSkillManageTool } from "./tools/skill-manage-tool.js";
|
|
36
41
|
import { createSoftwareUpdateTool } from "./tools/software-update-tool.js";
|
|
42
|
+
import { createSystemStatusTool } from "./tools/system-status-tool.js";
|
|
43
|
+
import { createUsageReportTool } from "./tools/usage-report-tool.js";
|
|
37
44
|
import { createVerifyContactTool, createVerifyContactCodeTool, } from "./tools/verify-contact-tool.js";
|
|
38
45
|
export function createTaskmasterTools(options) {
|
|
39
46
|
const imageTool = options?.agentDir?.trim()
|
|
@@ -152,6 +159,13 @@ export function createTaskmasterTools(options) {
|
|
|
152
159
|
createVerifyContactCodeTool({ agentSessionKey: options?.agentSessionKey }),
|
|
153
160
|
createRelayMessageTool(),
|
|
154
161
|
createApiKeysTool(),
|
|
162
|
+
createSystemStatusTool(),
|
|
163
|
+
createUsageReportTool(),
|
|
164
|
+
createChannelSettingsTool(),
|
|
165
|
+
createBrandSettingsTool(),
|
|
166
|
+
createPublicChatSettingsTool(),
|
|
167
|
+
createSkillManageTool(),
|
|
168
|
+
createLogsReadTool(),
|
|
155
169
|
createFileDeleteTool({
|
|
156
170
|
config: options?.config,
|
|
157
171
|
agentSessionKey: options?.agentSessionKey,
|
|
@@ -46,6 +46,16 @@ export const TOOL_GROUPS = {
|
|
|
46
46
|
],
|
|
47
47
|
// Admin management tools
|
|
48
48
|
"group:admin": ["authorize_admin", "revoke_admin", "list_admins"],
|
|
49
|
+
// Control panel tools — expose setup/dashboard functionality to agents
|
|
50
|
+
"group:control-panel": [
|
|
51
|
+
"system_status",
|
|
52
|
+
"usage_report",
|
|
53
|
+
"channel_settings",
|
|
54
|
+
"brand_settings",
|
|
55
|
+
"public_chat_settings",
|
|
56
|
+
"skill_manage",
|
|
57
|
+
"logs_read",
|
|
58
|
+
],
|
|
49
59
|
// All Taskmaster native tools (excludes provider plugins).
|
|
50
60
|
"group:taskmaster": [
|
|
51
61
|
"browser",
|
|
@@ -80,6 +90,13 @@ export const TOOL_GROUPS = {
|
|
|
80
90
|
"skill_draft_save",
|
|
81
91
|
"verify_contact",
|
|
82
92
|
"verify_contact_code",
|
|
93
|
+
"system_status",
|
|
94
|
+
"usage_report",
|
|
95
|
+
"channel_settings",
|
|
96
|
+
"brand_settings",
|
|
97
|
+
"public_chat_settings",
|
|
98
|
+
"skill_manage",
|
|
99
|
+
"logs_read",
|
|
83
100
|
],
|
|
84
101
|
};
|
|
85
102
|
// Tools that are never granted by profiles — must be explicitly added to the
|
|
@@ -123,6 +140,7 @@ const TOOL_PROFILES = {
|
|
|
123
140
|
"license_generate",
|
|
124
141
|
"group:contacts",
|
|
125
142
|
"group:skills",
|
|
143
|
+
"group:control-panel",
|
|
126
144
|
"relay_message",
|
|
127
145
|
"memory_save_media",
|
|
128
146
|
"image_generate",
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent tool for managing branding settings.
|
|
3
|
+
*
|
|
4
|
+
* Wraps `config.get`/`config.patch` for accent/background color and
|
|
5
|
+
* `brand.hasLogo`/`brand.removeLogo` for logo management. Logo upload
|
|
6
|
+
* is excluded — it requires binary data better handled through the UI.
|
|
7
|
+
*/
|
|
8
|
+
import { Type } from "@sinclair/typebox";
|
|
9
|
+
import { stringEnum } from "../schema/typebox.js";
|
|
10
|
+
import { jsonResult, readStringParam } from "./common.js";
|
|
11
|
+
import { callGatewayTool } from "./gateway.js";
|
|
12
|
+
const BRAND_ACTIONS = ["get", "set_colors", "remove_logo"];
|
|
13
|
+
const BrandSettingsSchema = Type.Object({
|
|
14
|
+
action: stringEnum(BRAND_ACTIONS, {
|
|
15
|
+
description: '"get" returns current branding (accent color, background color, logo status). ' +
|
|
16
|
+
'"set_colors" updates accent and/or background colors. ' +
|
|
17
|
+
'"remove_logo" removes the workspace logo.',
|
|
18
|
+
}),
|
|
19
|
+
workspace: Type.Optional(Type.String({
|
|
20
|
+
description: "Workspace name. Required for logo operations. Defaults to the active workspace.",
|
|
21
|
+
})),
|
|
22
|
+
accentColor: Type.Optional(Type.String({
|
|
23
|
+
description: 'Hex color for the accent/primary color (e.g. "#3B82F6"). Used with set_colors.',
|
|
24
|
+
})),
|
|
25
|
+
backgroundColor: Type.Optional(Type.String({
|
|
26
|
+
description: 'Hex color for the background (e.g. "#1A1A2E"). Used with set_colors.',
|
|
27
|
+
})),
|
|
28
|
+
});
|
|
29
|
+
export function createBrandSettingsTool() {
|
|
30
|
+
return {
|
|
31
|
+
label: "Brand Settings",
|
|
32
|
+
name: "brand_settings",
|
|
33
|
+
description: "Read and update branding settings. " +
|
|
34
|
+
'"get" returns current accent color, background color, and logo status. ' +
|
|
35
|
+
'"set_colors" updates accent and/or background colors (hex values). ' +
|
|
36
|
+
'"remove_logo" removes the workspace logo. ' +
|
|
37
|
+
"Logo upload requires the UI due to binary data — suggest the user upload through the Setup page.",
|
|
38
|
+
parameters: BrandSettingsSchema,
|
|
39
|
+
execute: async (_toolCallId, args) => {
|
|
40
|
+
const params = args;
|
|
41
|
+
const action = readStringParam(params, "action", { required: true });
|
|
42
|
+
const gatewayOpts = {};
|
|
43
|
+
if (action === "get") {
|
|
44
|
+
const config = await callGatewayTool("config.get", gatewayOpts, {});
|
|
45
|
+
let brand = {};
|
|
46
|
+
if (config && typeof config === "object" && typeof config.raw === "string") {
|
|
47
|
+
try {
|
|
48
|
+
const parsed = JSON.parse(config.raw);
|
|
49
|
+
brand = parsed?.brand ?? {};
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
// fallback to empty
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
// Also check logo status
|
|
56
|
+
const workspace = readStringParam(params, "workspace");
|
|
57
|
+
let hasLogo;
|
|
58
|
+
if (workspace) {
|
|
59
|
+
try {
|
|
60
|
+
const logoResult = await callGatewayTool("brand.hasLogo", gatewayOpts, { workspace });
|
|
61
|
+
hasLogo = logoResult?.hasLogo;
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
// non-critical
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return jsonResult({
|
|
68
|
+
accentColor: brand.accentColor ?? null,
|
|
69
|
+
backgroundColor: brand.backgroundColor ?? null,
|
|
70
|
+
...(hasLogo !== undefined ? { hasLogo } : {}),
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
if (action === "set_colors") {
|
|
74
|
+
const accentColor = readStringParam(params, "accentColor");
|
|
75
|
+
const backgroundColor = readStringParam(params, "backgroundColor");
|
|
76
|
+
if (!accentColor && !backgroundColor) {
|
|
77
|
+
throw new Error("At least one of accentColor or backgroundColor is required.");
|
|
78
|
+
}
|
|
79
|
+
const brandPatch = {};
|
|
80
|
+
if (accentColor)
|
|
81
|
+
brandPatch.accentColor = accentColor;
|
|
82
|
+
if (backgroundColor)
|
|
83
|
+
brandPatch.backgroundColor = backgroundColor;
|
|
84
|
+
const snapshot = await callGatewayTool("config.get", gatewayOpts, {});
|
|
85
|
+
const baseHash = typeof snapshot?.hash === "string" ? snapshot.hash : undefined;
|
|
86
|
+
const result = await callGatewayTool("config.patch", gatewayOpts, {
|
|
87
|
+
raw: JSON.stringify({ brand: brandPatch }),
|
|
88
|
+
baseHash,
|
|
89
|
+
note: "agent: update brand colors",
|
|
90
|
+
});
|
|
91
|
+
return jsonResult({ ok: true, ...brandPatch, result });
|
|
92
|
+
}
|
|
93
|
+
if (action === "remove_logo") {
|
|
94
|
+
const workspace = readStringParam(params, "workspace", { required: true, label: "workspace" });
|
|
95
|
+
const result = await callGatewayTool("brand.removeLogo", gatewayOpts, { workspace });
|
|
96
|
+
return jsonResult(result);
|
|
97
|
+
}
|
|
98
|
+
throw new Error(`Unknown action: ${action}`);
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent tool for reading and updating channel settings.
|
|
3
|
+
*
|
|
4
|
+
* Wraps `channels.status` for reading and `config.patch` for writing per-account
|
|
5
|
+
* WhatsApp/iMessage settings: model selection, thinking level, DM policy, and
|
|
6
|
+
* group chat policy. These are settings users normally adjust through the
|
|
7
|
+
* WhatsApp Settings modal in the control panel.
|
|
8
|
+
*/
|
|
9
|
+
import { Type } from "@sinclair/typebox";
|
|
10
|
+
import { stringEnum, optionalStringEnum } from "../schema/typebox.js";
|
|
11
|
+
import { jsonResult, readStringParam } from "./common.js";
|
|
12
|
+
import { callGatewayTool } from "./gateway.js";
|
|
13
|
+
const CHANNEL_SETTINGS_ACTIONS = ["status", "set_model", "set_thinking", "set_dm_policy", "set_group_policy"];
|
|
14
|
+
const DM_POLICY_VALUES = ["open", "closed"];
|
|
15
|
+
const GROUP_POLICY_VALUES = ["open", "disabled", "allowlist"];
|
|
16
|
+
const ChannelSettingsSchema = Type.Object({
|
|
17
|
+
action: stringEnum(CHANNEL_SETTINGS_ACTIONS, {
|
|
18
|
+
description: '"status" returns connection status for all channels. ' +
|
|
19
|
+
'"set_model" changes the AI model for a WhatsApp account. ' +
|
|
20
|
+
'"set_thinking" changes the thinking level. ' +
|
|
21
|
+
'"set_dm_policy" sets whether public DMs are handled (open/closed). ' +
|
|
22
|
+
'"set_group_policy" sets group chat mode (open/disabled/allowlist).',
|
|
23
|
+
}),
|
|
24
|
+
accountId: Type.Optional(Type.String({
|
|
25
|
+
description: "WhatsApp account ID (required for set_* actions). Use status action to discover account IDs.",
|
|
26
|
+
})),
|
|
27
|
+
model: Type.Optional(Type.String({
|
|
28
|
+
description: 'Model ID to set (e.g. "anthropic/claude-sonnet-4-5"). Required for set_model.',
|
|
29
|
+
})),
|
|
30
|
+
thinking: Type.Optional(Type.String({
|
|
31
|
+
description: 'Thinking level: "none", "low", "medium", "high". Required for set_thinking.',
|
|
32
|
+
})),
|
|
33
|
+
dmPolicy: optionalStringEnum(DM_POLICY_VALUES, {
|
|
34
|
+
description: 'DM policy: "open" (handle public DMs) or "closed" (ignore). Required for set_dm_policy.',
|
|
35
|
+
}),
|
|
36
|
+
groupPolicy: optionalStringEnum(GROUP_POLICY_VALUES, {
|
|
37
|
+
description: 'Group chat policy: "open", "disabled", or "allowlist". Required for set_group_policy.',
|
|
38
|
+
}),
|
|
39
|
+
});
|
|
40
|
+
export function createChannelSettingsTool() {
|
|
41
|
+
return {
|
|
42
|
+
label: "Channel Settings",
|
|
43
|
+
name: "channel_settings",
|
|
44
|
+
description: "Read and update channel settings (WhatsApp, iMessage). " +
|
|
45
|
+
'"status" shows connection state and account list. ' +
|
|
46
|
+
"set_model/set_thinking/set_dm_policy/set_group_policy update per-account WhatsApp settings. " +
|
|
47
|
+
"Use status first to discover available account IDs.",
|
|
48
|
+
parameters: ChannelSettingsSchema,
|
|
49
|
+
execute: async (_toolCallId, args) => {
|
|
50
|
+
const params = args;
|
|
51
|
+
const action = readStringParam(params, "action", { required: true });
|
|
52
|
+
const gatewayOpts = {};
|
|
53
|
+
if (action === "status") {
|
|
54
|
+
const result = await callGatewayTool("channels.status", gatewayOpts, {});
|
|
55
|
+
return jsonResult(result);
|
|
56
|
+
}
|
|
57
|
+
// All set_* actions require accountId and use config.patch
|
|
58
|
+
const accountId = readStringParam(params, "accountId", { required: true, label: "accountId" });
|
|
59
|
+
if (action === "set_model") {
|
|
60
|
+
const model = readStringParam(params, "model", { required: true, label: "model" });
|
|
61
|
+
// Patch the WhatsApp account's model in config
|
|
62
|
+
const snapshot = await callGatewayTool("config.get", gatewayOpts, {});
|
|
63
|
+
const baseHash = typeof snapshot?.hash === "string" ? snapshot.hash : undefined;
|
|
64
|
+
const result = await callGatewayTool("config.patch", gatewayOpts, {
|
|
65
|
+
raw: JSON.stringify({
|
|
66
|
+
channels: { whatsapp: { accounts: { [accountId]: { model } } } },
|
|
67
|
+
}),
|
|
68
|
+
baseHash,
|
|
69
|
+
note: `agent: set model to ${model} for account ${accountId}`,
|
|
70
|
+
});
|
|
71
|
+
return jsonResult({ ok: true, accountId, model, result });
|
|
72
|
+
}
|
|
73
|
+
if (action === "set_thinking") {
|
|
74
|
+
const thinking = readStringParam(params, "thinking", { required: true, label: "thinking" });
|
|
75
|
+
const snapshot = await callGatewayTool("config.get", gatewayOpts, {});
|
|
76
|
+
const baseHash = typeof snapshot?.hash === "string" ? snapshot.hash : undefined;
|
|
77
|
+
const result = await callGatewayTool("config.patch", gatewayOpts, {
|
|
78
|
+
raw: JSON.stringify({
|
|
79
|
+
channels: { whatsapp: { accounts: { [accountId]: { thinking } } } },
|
|
80
|
+
}),
|
|
81
|
+
baseHash,
|
|
82
|
+
note: `agent: set thinking to ${thinking} for account ${accountId}`,
|
|
83
|
+
});
|
|
84
|
+
return jsonResult({ ok: true, accountId, thinking, result });
|
|
85
|
+
}
|
|
86
|
+
if (action === "set_dm_policy") {
|
|
87
|
+
const dmPolicy = readStringParam(params, "dmPolicy", { required: true, label: "dmPolicy" });
|
|
88
|
+
const handlePublicDMs = dmPolicy === "open";
|
|
89
|
+
const snapshot = await callGatewayTool("config.get", gatewayOpts, {});
|
|
90
|
+
const baseHash = typeof snapshot?.hash === "string" ? snapshot.hash : undefined;
|
|
91
|
+
const result = await callGatewayTool("config.patch", gatewayOpts, {
|
|
92
|
+
raw: JSON.stringify({
|
|
93
|
+
channels: { whatsapp: { accounts: { [accountId]: { handlePublicDMs } } } },
|
|
94
|
+
}),
|
|
95
|
+
baseHash,
|
|
96
|
+
note: `agent: set DM policy to ${dmPolicy} for account ${accountId}`,
|
|
97
|
+
});
|
|
98
|
+
return jsonResult({ ok: true, accountId, dmPolicy, result });
|
|
99
|
+
}
|
|
100
|
+
if (action === "set_group_policy") {
|
|
101
|
+
const groupPolicy = readStringParam(params, "groupPolicy", { required: true, label: "groupPolicy" });
|
|
102
|
+
const snapshot = await callGatewayTool("config.get", gatewayOpts, {});
|
|
103
|
+
const baseHash = typeof snapshot?.hash === "string" ? snapshot.hash : undefined;
|
|
104
|
+
const result = await callGatewayTool("config.patch", gatewayOpts, {
|
|
105
|
+
raw: JSON.stringify({
|
|
106
|
+
channels: { whatsapp: { accounts: { [accountId]: { groups: { mode: groupPolicy } } } } },
|
|
107
|
+
}),
|
|
108
|
+
baseHash,
|
|
109
|
+
note: `agent: set group policy to ${groupPolicy} for account ${accountId}`,
|
|
110
|
+
});
|
|
111
|
+
return jsonResult({ ok: true, accountId, groupPolicy, result });
|
|
112
|
+
}
|
|
113
|
+
throw new Error(`Unknown action: ${action}`);
|
|
114
|
+
},
|
|
115
|
+
};
|
|
116
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent tool for reading system and session logs.
|
|
3
|
+
*
|
|
4
|
+
* Wraps `logs.tail` for system logs and `sessions.transcript` for session logs,
|
|
5
|
+
* giving the agent full observability into what the system has been doing —
|
|
6
|
+
* without the user needing to open the Advanced > Logs tab.
|
|
7
|
+
*/
|
|
8
|
+
import { Type } from "@sinclair/typebox";
|
|
9
|
+
import { stringEnum } from "../schema/typebox.js";
|
|
10
|
+
import { jsonResult, readStringParam, readStringArrayParam } from "./common.js";
|
|
11
|
+
import { callGatewayTool } from "./gateway.js";
|
|
12
|
+
const LOGS_ACTIONS = ["system", "sessions"];
|
|
13
|
+
const LogsReadSchema = Type.Object({
|
|
14
|
+
action: stringEnum(LOGS_ACTIONS, {
|
|
15
|
+
description: '"system" returns recent gateway system logs (errors, warnings, info). ' +
|
|
16
|
+
'"sessions" returns session transcript entries (messages, tool calls) across agents.',
|
|
17
|
+
}),
|
|
18
|
+
limit: Type.Optional(Type.Number({
|
|
19
|
+
description: "Maximum number of log lines/entries to return. Default 200 for system, 100 for sessions.",
|
|
20
|
+
})),
|
|
21
|
+
cursor: Type.Optional(Type.Number({
|
|
22
|
+
description: "Byte cursor for pagination (system logs). Returned in previous response.",
|
|
23
|
+
})),
|
|
24
|
+
agents: Type.Optional(Type.Array(Type.String(), {
|
|
25
|
+
description: 'Filter session logs to specific agent IDs (e.g. ["admin", "public"]). Sessions action only.',
|
|
26
|
+
})),
|
|
27
|
+
});
|
|
28
|
+
export function createLogsReadTool() {
|
|
29
|
+
return {
|
|
30
|
+
label: "Logs",
|
|
31
|
+
name: "logs_read",
|
|
32
|
+
description: "Read system and session logs. " +
|
|
33
|
+
'"system" returns recent gateway logs with cursor-based pagination. ' +
|
|
34
|
+
'"sessions" returns transcript entries (messages, tool calls, errors) across all agents. ' +
|
|
35
|
+
"Filter session logs by agent ID. Use the cursor from previous calls for pagination.",
|
|
36
|
+
parameters: LogsReadSchema,
|
|
37
|
+
execute: async (_toolCallId, args) => {
|
|
38
|
+
const params = args;
|
|
39
|
+
const action = readStringParam(params, "action", { required: true });
|
|
40
|
+
if (action === "system") {
|
|
41
|
+
const limit = typeof params.limit === "number" && Number.isFinite(params.limit)
|
|
42
|
+
? Math.max(1, Math.floor(params.limit))
|
|
43
|
+
: 200;
|
|
44
|
+
const cursor = typeof params.cursor === "number" && Number.isFinite(params.cursor)
|
|
45
|
+
? Math.max(0, Math.floor(params.cursor))
|
|
46
|
+
: undefined;
|
|
47
|
+
const result = await callGatewayTool("logs.tail", {}, {
|
|
48
|
+
limit,
|
|
49
|
+
...(cursor !== undefined ? { cursor } : {}),
|
|
50
|
+
});
|
|
51
|
+
return jsonResult(result);
|
|
52
|
+
}
|
|
53
|
+
if (action === "sessions") {
|
|
54
|
+
const limit = typeof params.limit === "number" && Number.isFinite(params.limit)
|
|
55
|
+
? Math.max(1, Math.floor(params.limit))
|
|
56
|
+
: 100;
|
|
57
|
+
const agents = readStringArrayParam(params, "agents");
|
|
58
|
+
const result = await callGatewayTool("sessions.transcript", {}, {
|
|
59
|
+
limit,
|
|
60
|
+
...(agents ? { agents } : {}),
|
|
61
|
+
});
|
|
62
|
+
return jsonResult(result);
|
|
63
|
+
}
|
|
64
|
+
throw new Error(`Unknown action: ${action}`);
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent tool for managing public chat settings.
|
|
3
|
+
*
|
|
4
|
+
* Wraps config operations for the public chat widget: enable/disable, greeting
|
|
5
|
+
* text, verification mode, and greeting regeneration. These are settings users
|
|
6
|
+
* normally adjust through the Public Chat Settings modal in the control panel.
|
|
7
|
+
*/
|
|
8
|
+
import { Type } from "@sinclair/typebox";
|
|
9
|
+
import { stringEnum, optionalStringEnum } from "../schema/typebox.js";
|
|
10
|
+
import { jsonResult, readStringParam } from "./common.js";
|
|
11
|
+
import { callGatewayTool } from "./gateway.js";
|
|
12
|
+
const PUBLIC_CHAT_ACTIONS = [
|
|
13
|
+
"status",
|
|
14
|
+
"enable",
|
|
15
|
+
"disable",
|
|
16
|
+
"set_greeting",
|
|
17
|
+
"regenerate_greeting",
|
|
18
|
+
"set_access_mode",
|
|
19
|
+
];
|
|
20
|
+
const ACCESS_MODES = ["open", "verified", "visitor-chooses"];
|
|
21
|
+
const PublicChatSettingsSchema = Type.Object({
|
|
22
|
+
action: stringEnum(PUBLIC_CHAT_ACTIONS, {
|
|
23
|
+
description: '"status" returns current public chat config (enabled, greeting, access mode). ' +
|
|
24
|
+
'"enable"/"disable" toggles public chat. ' +
|
|
25
|
+
'"set_greeting" updates the greeting text. ' +
|
|
26
|
+
'"regenerate_greeting" generates a new greeting using the agent persona. ' +
|
|
27
|
+
'"set_access_mode" sets visitor access: open, verified, or visitor-chooses.',
|
|
28
|
+
}),
|
|
29
|
+
greeting: Type.Optional(Type.String({
|
|
30
|
+
description: "Greeting text to display to visitors. Used with set_greeting.",
|
|
31
|
+
})),
|
|
32
|
+
greetingEnabled: Type.Optional(Type.Boolean({
|
|
33
|
+
description: "Whether to show a greeting message. Used with set_greeting.",
|
|
34
|
+
})),
|
|
35
|
+
accessMode: optionalStringEnum(ACCESS_MODES, {
|
|
36
|
+
description: '"open" allows anonymous access, "verified" requires phone/email verification, ' +
|
|
37
|
+
'"visitor-chooses" lets the visitor decide. Used with set_access_mode.',
|
|
38
|
+
}),
|
|
39
|
+
accountId: Type.Optional(Type.String({
|
|
40
|
+
description: "Account ID for greeting regeneration. Required for regenerate_greeting.",
|
|
41
|
+
})),
|
|
42
|
+
});
|
|
43
|
+
export function createPublicChatSettingsTool() {
|
|
44
|
+
return {
|
|
45
|
+
label: "Public Chat Settings",
|
|
46
|
+
name: "public_chat_settings",
|
|
47
|
+
description: "Manage the public chat widget settings. " +
|
|
48
|
+
'"status" returns current config. ' +
|
|
49
|
+
'"enable"/"disable" toggles the widget. ' +
|
|
50
|
+
'"set_greeting" updates greeting text and visibility. ' +
|
|
51
|
+
'"regenerate_greeting" auto-generates a greeting from the agent persona. ' +
|
|
52
|
+
'"set_access_mode" controls visitor verification (open/verified/visitor-chooses).',
|
|
53
|
+
parameters: PublicChatSettingsSchema,
|
|
54
|
+
execute: async (_toolCallId, args) => {
|
|
55
|
+
const params = args;
|
|
56
|
+
const action = readStringParam(params, "action", { required: true });
|
|
57
|
+
const gatewayOpts = {};
|
|
58
|
+
if (action === "status") {
|
|
59
|
+
const config = await callGatewayTool("config.get", gatewayOpts, {});
|
|
60
|
+
let publicChat = {};
|
|
61
|
+
if (config && typeof config === "object" && typeof config.raw === "string") {
|
|
62
|
+
try {
|
|
63
|
+
const parsed = JSON.parse(config.raw);
|
|
64
|
+
publicChat = parsed?.publicChat ?? {};
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
// fallback
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return jsonResult({
|
|
71
|
+
enabled: publicChat.enabled ?? false,
|
|
72
|
+
greeting: publicChat.greeting ?? null,
|
|
73
|
+
greetingEnabled: publicChat.greetingEnabled ?? true,
|
|
74
|
+
accessMode: publicChat.accessMode ?? "open",
|
|
75
|
+
verifyMethods: publicChat.verifyMethods ?? ["phone"],
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
if (action === "enable" || action === "disable") {
|
|
79
|
+
const enabled = action === "enable";
|
|
80
|
+
const snapshot = await callGatewayTool("config.get", gatewayOpts, {});
|
|
81
|
+
const baseHash = typeof snapshot?.hash === "string" ? snapshot.hash : undefined;
|
|
82
|
+
const result = await callGatewayTool("config.patch", gatewayOpts, {
|
|
83
|
+
raw: JSON.stringify({ publicChat: { enabled } }),
|
|
84
|
+
baseHash,
|
|
85
|
+
note: `agent: ${action} public chat`,
|
|
86
|
+
});
|
|
87
|
+
return jsonResult({ ok: true, enabled, result });
|
|
88
|
+
}
|
|
89
|
+
if (action === "set_greeting") {
|
|
90
|
+
const greeting = readStringParam(params, "greeting");
|
|
91
|
+
const greetingEnabled = typeof params.greetingEnabled === "boolean" ? params.greetingEnabled : undefined;
|
|
92
|
+
if (greeting === undefined && greetingEnabled === undefined) {
|
|
93
|
+
throw new Error("At least one of greeting or greetingEnabled is required.");
|
|
94
|
+
}
|
|
95
|
+
const patch = {};
|
|
96
|
+
if (greeting !== undefined)
|
|
97
|
+
patch.greeting = greeting;
|
|
98
|
+
if (greetingEnabled !== undefined)
|
|
99
|
+
patch.greetingEnabled = greetingEnabled;
|
|
100
|
+
const snapshot = await callGatewayTool("config.get", gatewayOpts, {});
|
|
101
|
+
const baseHash = typeof snapshot?.hash === "string" ? snapshot.hash : undefined;
|
|
102
|
+
const result = await callGatewayTool("config.patch", gatewayOpts, {
|
|
103
|
+
raw: JSON.stringify({ publicChat: patch }),
|
|
104
|
+
baseHash,
|
|
105
|
+
note: "agent: update public chat greeting",
|
|
106
|
+
});
|
|
107
|
+
return jsonResult({ ok: true, ...patch, result });
|
|
108
|
+
}
|
|
109
|
+
if (action === "regenerate_greeting") {
|
|
110
|
+
const accountId = readStringParam(params, "accountId", { required: true, label: "accountId" });
|
|
111
|
+
const result = await callGatewayTool("public.greeting.generate", gatewayOpts, { accountId });
|
|
112
|
+
return jsonResult(result);
|
|
113
|
+
}
|
|
114
|
+
if (action === "set_access_mode") {
|
|
115
|
+
const accessMode = readStringParam(params, "accessMode", { required: true, label: "accessMode" });
|
|
116
|
+
const snapshot = await callGatewayTool("config.get", gatewayOpts, {});
|
|
117
|
+
const baseHash = typeof snapshot?.hash === "string" ? snapshot.hash : undefined;
|
|
118
|
+
const result = await callGatewayTool("config.patch", gatewayOpts, {
|
|
119
|
+
raw: JSON.stringify({ publicChat: { accessMode } }),
|
|
120
|
+
baseHash,
|
|
121
|
+
note: `agent: set public chat access mode to ${accessMode}`,
|
|
122
|
+
});
|
|
123
|
+
return jsonResult({ ok: true, accessMode, result });
|
|
124
|
+
}
|
|
125
|
+
throw new Error(`Unknown action: ${action}`);
|
|
126
|
+
},
|
|
127
|
+
};
|
|
128
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent tool for managing skills (create, update, delete, install).
|
|
3
|
+
*
|
|
4
|
+
* Complements the existing `skill_read` and `skill_draft_save` tools by adding
|
|
5
|
+
* write operations. The agent can now manage the full skill lifecycle through
|
|
6
|
+
* conversation rather than requiring the user to navigate the Advanced > Skills UI.
|
|
7
|
+
*/
|
|
8
|
+
import { Type } from "@sinclair/typebox";
|
|
9
|
+
import { stringEnum } from "../schema/typebox.js";
|
|
10
|
+
import { jsonResult, readStringParam } from "./common.js";
|
|
11
|
+
import { callGatewayTool } from "./gateway.js";
|
|
12
|
+
const SKILL_MANAGE_ACTIONS = ["list", "create", "update", "delete", "install"];
|
|
13
|
+
const SkillManageSchema = Type.Object({
|
|
14
|
+
action: stringEnum(SKILL_MANAGE_ACTIONS, {
|
|
15
|
+
description: '"list" returns all skills with their status. ' +
|
|
16
|
+
'"create" creates a new user skill (name + skillContent required). ' +
|
|
17
|
+
'"update" modifies a skill setting (skillKey required, plus enabled/apiKey/env). ' +
|
|
18
|
+
'"delete" removes a user skill (name required; preloaded skills cannot be deleted). ' +
|
|
19
|
+
'"install" installs a skill from a remote source (name + installId required).',
|
|
20
|
+
}),
|
|
21
|
+
name: Type.Optional(Type.String({
|
|
22
|
+
description: "Skill name/key (required for create, delete, install).",
|
|
23
|
+
})),
|
|
24
|
+
skillKey: Type.Optional(Type.String({
|
|
25
|
+
description: "Skill key for update action.",
|
|
26
|
+
})),
|
|
27
|
+
skillContent: Type.Optional(Type.String({
|
|
28
|
+
description: "SKILL.md content (required for create). Must include YAML frontmatter with name and description.",
|
|
29
|
+
})),
|
|
30
|
+
references: Type.Optional(Type.Array(Type.Object({
|
|
31
|
+
name: Type.String({ description: "Reference filename (e.g. details.md)." }),
|
|
32
|
+
content: Type.String({ description: "Reference file content." }),
|
|
33
|
+
}))),
|
|
34
|
+
enabled: Type.Optional(Type.Boolean({
|
|
35
|
+
description: "Enable or disable a skill (used with update).",
|
|
36
|
+
})),
|
|
37
|
+
apiKey: Type.Optional(Type.String({
|
|
38
|
+
description: "API key for a skill that requires one (used with update).",
|
|
39
|
+
})),
|
|
40
|
+
installId: Type.Optional(Type.String({
|
|
41
|
+
description: "Install identifier for remote skill installation (used with install).",
|
|
42
|
+
})),
|
|
43
|
+
});
|
|
44
|
+
export function createSkillManageTool() {
|
|
45
|
+
return {
|
|
46
|
+
label: "Skill Management",
|
|
47
|
+
name: "skill_manage",
|
|
48
|
+
description: "Manage workspace skills: list, create, update, delete, or install. " +
|
|
49
|
+
'"list" shows all skills and their status. ' +
|
|
50
|
+
'"create" makes a new skill (provide name + skillContent with YAML frontmatter). ' +
|
|
51
|
+
'"update" modifies settings (enabled, apiKey). ' +
|
|
52
|
+
'"delete" removes a user skill (preloaded skills are protected). ' +
|
|
53
|
+
'"install" adds a skill from a remote source.',
|
|
54
|
+
parameters: SkillManageSchema,
|
|
55
|
+
execute: async (_toolCallId, args) => {
|
|
56
|
+
const params = args;
|
|
57
|
+
const action = readStringParam(params, "action", { required: true });
|
|
58
|
+
if (action === "list") {
|
|
59
|
+
const result = await callGatewayTool("skills.status", {}, {});
|
|
60
|
+
return jsonResult(result);
|
|
61
|
+
}
|
|
62
|
+
if (action === "create") {
|
|
63
|
+
const name = readStringParam(params, "name", { required: true, label: "name" });
|
|
64
|
+
const skillContent = readStringParam(params, "skillContent", {
|
|
65
|
+
required: true,
|
|
66
|
+
label: "skillContent",
|
|
67
|
+
});
|
|
68
|
+
const references = Array.isArray(params.references) ? params.references : undefined;
|
|
69
|
+
const result = await callGatewayTool("skills.create", {}, {
|
|
70
|
+
name,
|
|
71
|
+
skillContent,
|
|
72
|
+
...(references ? { references } : {}),
|
|
73
|
+
});
|
|
74
|
+
return jsonResult(result);
|
|
75
|
+
}
|
|
76
|
+
if (action === "update") {
|
|
77
|
+
const skillKey = readStringParam(params, "skillKey") ?? readStringParam(params, "name");
|
|
78
|
+
if (!skillKey)
|
|
79
|
+
throw new Error("skillKey (or name) required for update");
|
|
80
|
+
const patch = { skillKey };
|
|
81
|
+
if (typeof params.enabled === "boolean")
|
|
82
|
+
patch.enabled = params.enabled;
|
|
83
|
+
if (typeof params.apiKey === "string")
|
|
84
|
+
patch.apiKey = params.apiKey;
|
|
85
|
+
const result = await callGatewayTool("skills.update", {}, patch);
|
|
86
|
+
return jsonResult(result);
|
|
87
|
+
}
|
|
88
|
+
if (action === "delete") {
|
|
89
|
+
const name = readStringParam(params, "name", { required: true, label: "name" });
|
|
90
|
+
const result = await callGatewayTool("skills.delete", {}, { name });
|
|
91
|
+
return jsonResult(result);
|
|
92
|
+
}
|
|
93
|
+
if (action === "install") {
|
|
94
|
+
const name = readStringParam(params, "name", { required: true, label: "name" });
|
|
95
|
+
const installId = readStringParam(params, "installId", {
|
|
96
|
+
required: true,
|
|
97
|
+
label: "installId",
|
|
98
|
+
});
|
|
99
|
+
const result = await callGatewayTool("skills.install", {}, { name, installId });
|
|
100
|
+
return jsonResult(result);
|
|
101
|
+
}
|
|
102
|
+
throw new Error(`Unknown action: ${action}`);
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent tool for checking system status.
|
|
3
|
+
*
|
|
4
|
+
* Gives the agent a unified view of gateway health, auth status, license info,
|
|
5
|
+
* channel connections, available models, and software update availability —
|
|
6
|
+
* everything a user would normally check by opening the Setup dashboard.
|
|
7
|
+
*/
|
|
8
|
+
import { Type } from "@sinclair/typebox";
|
|
9
|
+
import { stringEnum } from "../schema/typebox.js";
|
|
10
|
+
import { jsonResult, readStringParam } from "./common.js";
|
|
11
|
+
import { callGatewayTool } from "./gateway.js";
|
|
12
|
+
const SYSTEM_STATUS_ACTIONS = ["overview", "health", "auth", "license", "channels", "models", "update"];
|
|
13
|
+
const SystemStatusSchema = Type.Object({
|
|
14
|
+
action: stringEnum(SYSTEM_STATUS_ACTIONS, {
|
|
15
|
+
description: '"overview" returns a combined snapshot of all subsystems. Use specific actions for detailed info on one subsystem.',
|
|
16
|
+
}),
|
|
17
|
+
});
|
|
18
|
+
export function createSystemStatusTool() {
|
|
19
|
+
return {
|
|
20
|
+
label: "System Status",
|
|
21
|
+
name: "system_status",
|
|
22
|
+
description: "Check the status of the Taskmaster system. " +
|
|
23
|
+
'"overview" returns a combined snapshot (health, auth, license, channels, version, update availability). ' +
|
|
24
|
+
"Use specific actions for detail: " +
|
|
25
|
+
'"health" (gateway health), "auth" (Claude API auth), "license" (license info), ' +
|
|
26
|
+
'"channels" (WhatsApp/iMessage connections), "models" (available LLM models), ' +
|
|
27
|
+
'"update" (software version and update availability).',
|
|
28
|
+
parameters: SystemStatusSchema,
|
|
29
|
+
execute: async (_toolCallId, args) => {
|
|
30
|
+
const params = args;
|
|
31
|
+
const action = readStringParam(params, "action", { required: true });
|
|
32
|
+
if (action === "overview") {
|
|
33
|
+
// Fetch all subsystems in parallel for a unified snapshot
|
|
34
|
+
const [health, auth, license, channels, update] = await Promise.allSettled([
|
|
35
|
+
callGatewayTool("health", {}, {}),
|
|
36
|
+
callGatewayTool("auth.status", {}, {}),
|
|
37
|
+
callGatewayTool("license.status", {}, {}),
|
|
38
|
+
callGatewayTool("channels.status", {}, {}),
|
|
39
|
+
callGatewayTool("update.status", {}, {}),
|
|
40
|
+
]);
|
|
41
|
+
return jsonResult({
|
|
42
|
+
health: health.status === "fulfilled" ? health.value : { error: String(health.reason) },
|
|
43
|
+
auth: auth.status === "fulfilled" ? auth.value : { error: String(auth.reason) },
|
|
44
|
+
license: license.status === "fulfilled" ? license.value : { error: String(license.reason) },
|
|
45
|
+
channels: channels.status === "fulfilled" ? channels.value : { error: String(channels.reason) },
|
|
46
|
+
update: update.status === "fulfilled" ? update.value : { error: String(update.reason) },
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
const methodMap = {
|
|
50
|
+
health: "health",
|
|
51
|
+
auth: "auth.status",
|
|
52
|
+
license: "license.status",
|
|
53
|
+
channels: "channels.status",
|
|
54
|
+
models: "models.list",
|
|
55
|
+
update: "update.status",
|
|
56
|
+
};
|
|
57
|
+
const method = methodMap[action];
|
|
58
|
+
if (!method)
|
|
59
|
+
throw new Error(`Unknown action: ${action}`);
|
|
60
|
+
const result = await callGatewayTool(method, {}, {});
|
|
61
|
+
return jsonResult(result);
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
}
|