@rubytech/taskmaster 1.16.3 → 1.17.4
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/tools/logs-read-tool.js +9 -0
- package/dist/agents/tools/memory-tool.js +1 -0
- package/dist/agents/workspace-migrations.js +61 -0
- package/dist/auto-reply/group-activation.js +2 -0
- package/dist/auto-reply/reply/commands-session.js +28 -11
- package/dist/build-info.json +3 -3
- package/dist/config/agent-tools-reconcile.js +58 -0
- package/dist/config/group-policy.js +16 -0
- package/dist/config/zod-schema.providers-whatsapp.js +2 -0
- package/dist/control-ui/assets/index-XqRo9tNW.css +1 -0
- package/dist/control-ui/assets/{index-Bd75cI7J.js → index-koe4eKhk.js} +526 -493
- package/dist/control-ui/assets/index-koe4eKhk.js.map +1 -0
- package/dist/control-ui/index.html +2 -2
- package/dist/cron/preloaded.js +27 -23
- package/dist/cron/service/timer.js +5 -1
- package/dist/gateway/protocol/index.js +7 -2
- package/dist/gateway/protocol/schema/logs-chat.js +6 -0
- package/dist/gateway/protocol/schema/protocol-schemas.js +6 -0
- package/dist/gateway/protocol/schema/sessions-transcript.js +1 -0
- package/dist/gateway/protocol/schema/sessions.js +6 -1
- package/dist/gateway/protocol/schema/whatsapp.js +24 -0
- package/dist/gateway/protocol/schema.js +1 -0
- package/dist/gateway/public-chat/session-token.js +52 -0
- package/dist/gateway/public-chat-api.js +40 -13
- package/dist/gateway/server-methods/apikeys.js +2 -0
- package/dist/gateway/server-methods/logs.js +17 -1
- package/dist/gateway/server-methods/public-chat.js +5 -0
- package/dist/gateway/server-methods/sessions-transcript.js +30 -6
- package/dist/gateway/server-methods/whatsapp-conversations.js +387 -0
- package/dist/gateway/server-methods-list.js +6 -0
- package/dist/gateway/server-methods.js +7 -0
- package/dist/gateway/server.impl.js +19 -2
- package/dist/gateway/sessions-patch.js +1 -1
- package/dist/hooks/bundled/ride-dispatch/HOOK.md +7 -6
- package/dist/hooks/bundled/ride-dispatch/handler.js +98 -39
- package/dist/memory/manager.js +3 -3
- package/dist/tui/tui-command-handlers.js +1 -1
- package/dist/web/auto-reply/monitor/group-activation.js +12 -10
- package/dist/web/auto-reply/monitor/group-gating.js +23 -2
- package/dist/web/auto-reply/monitor/on-message.js +27 -5
- package/dist/web/auto-reply/monitor/process-message.js +64 -53
- package/dist/web/inbound/monitor.js +30 -0
- package/extensions/whatsapp/src/channel.ts +1 -1
- package/package.json +1 -1
- package/skills/log-review/SKILL.md +17 -4
- package/skills/log-review/references/review-protocol.md +4 -4
- package/taskmaster-docs/USER-GUIDE.md +14 -0
- package/templates/beagle-zanzibar/agents/admin/AGENTS.md +16 -8
- package/templates/beagle-zanzibar/agents/public/AGENTS.md +10 -5
- package/dist/control-ui/assets/index-Bd75cI7J.js.map +0 -1
- package/dist/control-ui/assets/index-BkymP95Y.css +0 -1
|
@@ -24,6 +24,9 @@ const LogsReadSchema = Type.Object({
|
|
|
24
24
|
agents: Type.Optional(Type.Array(Type.String(), {
|
|
25
25
|
description: 'Filter session logs to specific agent IDs (e.g. ["admin", "public"]). Sessions action only.',
|
|
26
26
|
})),
|
|
27
|
+
minLevel: Type.Optional(stringEnum(["fatal", "error", "warn", "info"], {
|
|
28
|
+
description: 'Filter system log entries to this severity or above. Use "warn" for health checks to avoid context overflow. Only applies to action: "system".',
|
|
29
|
+
})),
|
|
27
30
|
});
|
|
28
31
|
export function createLogsReadTool() {
|
|
29
32
|
return {
|
|
@@ -44,19 +47,25 @@ export function createLogsReadTool() {
|
|
|
44
47
|
const cursor = typeof params.cursor === "number" && Number.isFinite(params.cursor)
|
|
45
48
|
? Math.max(0, Math.floor(params.cursor))
|
|
46
49
|
: undefined;
|
|
50
|
+
const minLevel = typeof params.minLevel === "string" ? params.minLevel : undefined;
|
|
47
51
|
const result = await callGatewayTool("logs.tail", {}, {
|
|
48
52
|
limit,
|
|
49
53
|
...(cursor !== undefined ? { cursor } : {}),
|
|
54
|
+
...(minLevel !== undefined ? { minLevel } : {}),
|
|
50
55
|
});
|
|
51
56
|
return jsonResult(result);
|
|
52
57
|
}
|
|
53
58
|
if (action === "sessions") {
|
|
59
|
+
if (params.minLevel !== undefined) {
|
|
60
|
+
throw new Error('minLevel is not supported for action "sessions"');
|
|
61
|
+
}
|
|
54
62
|
const limit = typeof params.limit === "number" && Number.isFinite(params.limit)
|
|
55
63
|
? Math.max(1, Math.floor(params.limit))
|
|
56
64
|
: 100;
|
|
57
65
|
const agents = readStringArrayParam(params, "agents");
|
|
58
66
|
const result = await callGatewayTool("sessions.transcript", {}, {
|
|
59
67
|
limit,
|
|
68
|
+
errorsOnly: true,
|
|
60
69
|
...(agents ? { agents } : {}),
|
|
61
70
|
});
|
|
62
71
|
return jsonResult(result);
|
|
@@ -148,6 +148,7 @@ export function createMemoryWriteTool(options) {
|
|
|
148
148
|
description: "Write or append content to a file in the memory/ directory. " +
|
|
149
149
|
"Path must be within the session's allowed scope. " +
|
|
150
150
|
"Phone numbers in paths MUST include the + prefix (e.g., memory/users/+447734875155/profile.md). " +
|
|
151
|
+
"Group IDs in paths MUST include the @g.us suffix (e.g., memory/groups/120363425419890848@g.us/context.md). " +
|
|
151
152
|
"Creates parent directories if needed. Use mode='append' to add to existing content.",
|
|
152
153
|
parameters: MemoryWriteSchema,
|
|
153
154
|
execute: async (_toolCallId, params) => {
|
|
@@ -631,6 +631,65 @@ async function patchBeagleAdminDispatchInstructions(_agentsPath, content) {
|
|
|
631
631
|
}
|
|
632
632
|
return result;
|
|
633
633
|
}
|
|
634
|
+
// ---------------------------------------------------------------------------
|
|
635
|
+
// Migration: Beagle Zanzibar — Public agent tourist_phone field name (v1.17)
|
|
636
|
+
// ---------------------------------------------------------------------------
|
|
637
|
+
//
|
|
638
|
+
// Replaces `tourist_id:` with `tourist_phone:` in the dispatch file format
|
|
639
|
+
// examples and the Step 0 description. The hook parser looks for `tourist_phone`
|
|
640
|
+
// and previously would receive "unknown" when the agent wrote `tourist_id`.
|
|
641
|
+
async function patchBeaglePublicTouristPhone(_agentsPath, content) {
|
|
642
|
+
if (!isBeaglePublicAgent(content))
|
|
643
|
+
return null;
|
|
644
|
+
// Already migrated (or never had tourist_id)?
|
|
645
|
+
if (!content.includes("tourist_id"))
|
|
646
|
+
return null;
|
|
647
|
+
return content
|
|
648
|
+
.replace(/tourist_id: \[the tourist's phone number[^\]]*\]/g, "tourist_phone: [the tourist's phone number — from WhatsApp session or OTP-verified phone]")
|
|
649
|
+
.replace(/tourist_id: \[same identifier used in trip-request\]/g, "tourist_phone: [same phone used in trip-request]")
|
|
650
|
+
.replace(/Use it as the `tourist_id` in dispatch files\./g, "Use it as `tourist_phone` in dispatch files.")
|
|
651
|
+
.replace(/tourist_id:/g, "tourist_phone:");
|
|
652
|
+
}
|
|
653
|
+
// ---------------------------------------------------------------------------
|
|
654
|
+
// Migration: Beagle Zanzibar — Admin Driver Reply explicit message tool (v1.17)
|
|
655
|
+
// ---------------------------------------------------------------------------
|
|
656
|
+
//
|
|
657
|
+
// Replaces the vague "compile and send offers to the tourist" Driver Reply
|
|
658
|
+
// instruction with an explicit step-by-step that uses the `message` tool
|
|
659
|
+
// directly. Also updates Trip Request step 7 from "write offers dispatch file"
|
|
660
|
+
// to "message tourist directly" — the old approach created a file that nothing
|
|
661
|
+
// processed, stalling the booking flow.
|
|
662
|
+
const ADMIN_DRIVER_REPLY_OLD = `Process the driver's message in the context of the ongoing negotiation. If it's a fare quote, note it. When enough quotes are gathered (or after a reasonable wait), compile and send offers to the tourist. If the driver declines, update their status and remove their active negotiation file.`;
|
|
663
|
+
const ADMIN_DRIVER_REPLY_NEW = `1. If the driver is quoting a fare: record it in their memory profile (\`memory_write\` on \`drivers/{name}.md\`)
|
|
664
|
+
2. When all expected quotes are received (or after a reasonable wait): compile the offers and message the tourist directly using the \`message\` tool at the \`tourist_phone\` from the trip-request earlier in this session — do NOT write a dispatch file
|
|
665
|
+
3. If the driver is declining: update their status in memory to \`idle\` and delete their \`shared/active-negotiations/{phone-digits}.md\` file`;
|
|
666
|
+
const ADMIN_TRIP_REQUEST_OFFERS_OLD = `8. Message the tourist with up to 3 competing offers — fare, rating, vehicle type, journey time. Do NOT reveal driver name, phone, or plate (gated by payment)`;
|
|
667
|
+
const ADMIN_TRIP_REQUEST_OFFERS_NEW = `7. Message the tourist at \`tourist_phone\` using the \`message\` tool with \`accountId\` from this dispatch
|
|
668
|
+
- Include up to 3 offers: fare, vehicle type, driver rating, estimated journey time
|
|
669
|
+
- Do NOT reveal driver name, phone, or plate — those are gated by payment
|
|
670
|
+
- Cross-agent echo will relay the WhatsApp message to the tourist's active session automatically
|
|
671
|
+
- Do NOT write a dispatch file for the offers — message directly`;
|
|
672
|
+
async function patchBeagleAdminDriverReplyExplicit(_agentsPath, content) {
|
|
673
|
+
if (!isBeagleTaxiAdmin(content))
|
|
674
|
+
return null;
|
|
675
|
+
// Already migrated?
|
|
676
|
+
if (content.includes("do NOT write a dispatch file"))
|
|
677
|
+
return null;
|
|
678
|
+
// Only migrate if the old vague text is present
|
|
679
|
+
if (!content.includes(ADMIN_DRIVER_REPLY_OLD))
|
|
680
|
+
return null;
|
|
681
|
+
let result = content.replace(ADMIN_DRIVER_REPLY_OLD, ADMIN_DRIVER_REPLY_NEW);
|
|
682
|
+
// Also update Trip Request offers step if old text present
|
|
683
|
+
if (result.includes(ADMIN_TRIP_REQUEST_OFFERS_OLD)) {
|
|
684
|
+
// Remove preceding step 7 ("Message the tourist confirming...") and update step 8
|
|
685
|
+
result = result
|
|
686
|
+
.replace("6. Message the tourist confirming drivers have been contacted and quotes are being gathered\n7. When driver replies arrive (dispatched as `[System: Ride Dispatch — Driver Reply]`), compile offers\n" +
|
|
687
|
+
ADMIN_TRIP_REQUEST_OFFERS_OLD, "6. When driver replies arrive (dispatched as `[System: Ride Dispatch — Driver Reply]`), compile offers\n" +
|
|
688
|
+
ADMIN_TRIP_REQUEST_OFFERS_NEW)
|
|
689
|
+
.replace(ADMIN_TRIP_REQUEST_OFFERS_OLD, ADMIN_TRIP_REQUEST_OFFERS_NEW);
|
|
690
|
+
}
|
|
691
|
+
return result;
|
|
692
|
+
}
|
|
634
693
|
const MIGRATIONS = [
|
|
635
694
|
{ name: "skill-recommendations", apply: patchSkillRecommendations },
|
|
636
695
|
{ name: "owner-learning", apply: patchOwnerLearning },
|
|
@@ -645,6 +704,8 @@ const MIGRATIONS = [
|
|
|
645
704
|
{ name: "beagle-public-dispatch-workflow", apply: patchBeaglePublicDispatchWorkflow },
|
|
646
705
|
{ name: "beagle-public-tools-dispatch", apply: patchBeaglePublicToolsDispatch },
|
|
647
706
|
{ name: "beagle-admin-dispatch-instructions", apply: patchBeagleAdminDispatchInstructions },
|
|
707
|
+
{ name: "beagle-public-tourist-phone", apply: patchBeaglePublicTouristPhone },
|
|
708
|
+
{ name: "beagle-admin-driver-reply-explicit", apply: patchBeagleAdminDriverReplyExplicit },
|
|
648
709
|
];
|
|
649
710
|
/**
|
|
650
711
|
* Run all workspace migrations for every configured agent.
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { abortEmbeddedPiRun } from "../../agents/pi-embedded.js";
|
|
2
|
+
import { loadConfig, writeConfigFile } from "../../config/config.js";
|
|
2
3
|
import { updateSessionStore } from "../../config/sessions.js";
|
|
3
4
|
import { logVerbose } from "../../globals.js";
|
|
4
5
|
import { createInternalHookEvent, triggerInternalHook } from "../../hooks/internal-hooks.js";
|
|
5
6
|
import { scheduleGatewaySigusr1Restart, triggerTaskmasterRestart } from "../../infra/restart.js";
|
|
7
|
+
import { normalizeAccountId } from "../../routing/session-key.js";
|
|
6
8
|
import { parseActivationCommand } from "../group-activation.js";
|
|
7
9
|
import { parseSendPolicyCommand } from "../send-policy.js";
|
|
8
10
|
import { normalizeUsageDisplay, resolveResponseUsageMode } from "../thinking.js";
|
|
@@ -51,20 +53,35 @@ export const handleActivationCommand = async (params, allowTextCommands) => {
|
|
|
51
53
|
if (!activationCommand.mode) {
|
|
52
54
|
return {
|
|
53
55
|
shouldContinue: false,
|
|
54
|
-
reply: { text: "⚙️ Usage: /activation mention|always" },
|
|
56
|
+
reply: { text: "⚙️ Usage: /activation mention|always|off" },
|
|
55
57
|
};
|
|
56
58
|
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
59
|
+
const groupId = params.ctx.From ?? params.sessionKey;
|
|
60
|
+
const accountId = normalizeAccountId(params.ctx.AccountId);
|
|
61
|
+
const cfg = loadConfig();
|
|
62
|
+
if (!cfg.channels)
|
|
63
|
+
cfg.channels = {};
|
|
64
|
+
if (!cfg.channels.whatsapp)
|
|
65
|
+
cfg.channels.whatsapp = {};
|
|
66
|
+
const wa = cfg.channels.whatsapp;
|
|
67
|
+
if (accountId !== "default") {
|
|
68
|
+
if (!wa.accounts)
|
|
69
|
+
wa.accounts = {};
|
|
70
|
+
const accounts = wa.accounts;
|
|
71
|
+
if (!accounts[accountId])
|
|
72
|
+
accounts[accountId] = {};
|
|
73
|
+
if (!accounts[accountId].groups)
|
|
74
|
+
accounts[accountId].groups = {};
|
|
75
|
+
const groups = accounts[accountId].groups;
|
|
76
|
+
groups[groupId] = { ...groups[groupId], activation: activationCommand.mode };
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
if (!wa.groups)
|
|
80
|
+
wa.groups = {};
|
|
81
|
+
const groups = wa.groups;
|
|
82
|
+
groups[groupId] = { ...groups[groupId], activation: activationCommand.mode };
|
|
67
83
|
}
|
|
84
|
+
await writeConfigFile(cfg);
|
|
68
85
|
return {
|
|
69
86
|
shouldContinue: false,
|
|
70
87
|
reply: {
|
package/dist/build-info.json
CHANGED
|
@@ -156,6 +156,64 @@ export function reconcileQrGenerateTool(params) {
|
|
|
156
156
|
}
|
|
157
157
|
return { config, changes };
|
|
158
158
|
}
|
|
159
|
+
/**
|
|
160
|
+
* Individual control-panel tool names that should be replaced by `group:control-panel`.
|
|
161
|
+
* Matches the members of TOOL_GROUPS["group:control-panel"] in tool-policy.ts.
|
|
162
|
+
*/
|
|
163
|
+
const INDIVIDUAL_CONTROL_PANEL_TOOLS = [
|
|
164
|
+
"system_status",
|
|
165
|
+
"usage_report",
|
|
166
|
+
"channel_settings",
|
|
167
|
+
"brand_settings",
|
|
168
|
+
"opening_hours",
|
|
169
|
+
"public_chat_settings",
|
|
170
|
+
"skill_manage",
|
|
171
|
+
"logs_read",
|
|
172
|
+
];
|
|
173
|
+
/**
|
|
174
|
+
* Ensure admin agents use `group:control-panel` instead of individual control-panel tools.
|
|
175
|
+
*
|
|
176
|
+
* Agents set up before `group:control-panel` was introduced have individual tool
|
|
177
|
+
* entries in their explicit allow lists and therefore miss tools added to the group
|
|
178
|
+
* later (e.g. `logs_read`). This reconciliation replaces those individual entries
|
|
179
|
+
* with the group reference so the agent always sees the full, current set.
|
|
180
|
+
*
|
|
181
|
+
* Runs unconditionally on gateway startup. Idempotent — skips agents that already
|
|
182
|
+
* have `group:control-panel` or have no individual control-panel tools.
|
|
183
|
+
*/
|
|
184
|
+
export function reconcileControlPanelTools(params) {
|
|
185
|
+
const config = structuredClone(params.config);
|
|
186
|
+
const changes = [];
|
|
187
|
+
const agents = config.agents?.list;
|
|
188
|
+
if (!Array.isArray(agents))
|
|
189
|
+
return { config, changes };
|
|
190
|
+
for (const agent of agents) {
|
|
191
|
+
if (!agent || !isAdminAgent(agent))
|
|
192
|
+
continue;
|
|
193
|
+
const allow = agent.tools?.allow;
|
|
194
|
+
if (!Array.isArray(allow))
|
|
195
|
+
continue;
|
|
196
|
+
// Already using the group — nothing to do
|
|
197
|
+
if (allow.includes("group:control-panel"))
|
|
198
|
+
continue;
|
|
199
|
+
// Check if any individual control-panel tools are present
|
|
200
|
+
const hasAny = INDIVIDUAL_CONTROL_PANEL_TOOLS.some((t) => allow.includes(t));
|
|
201
|
+
if (!hasAny)
|
|
202
|
+
continue;
|
|
203
|
+
// Remove individual entries, add group
|
|
204
|
+
const removed = [];
|
|
205
|
+
for (const tool of INDIVIDUAL_CONTROL_PANEL_TOOLS) {
|
|
206
|
+
const idx = allow.indexOf(tool);
|
|
207
|
+
if (idx !== -1) {
|
|
208
|
+
allow.splice(idx, 1);
|
|
209
|
+
removed.push(tool);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
allow.push("group:control-panel");
|
|
213
|
+
changes.push(`Replaced ${removed.join(", ")} with group:control-panel in agent "${agent.id}" tools.allow.`);
|
|
214
|
+
}
|
|
215
|
+
return { config, changes };
|
|
216
|
+
}
|
|
159
217
|
/**
|
|
160
218
|
* Remove privileged tools (`message`, `contact_lookup`, `group:contacts`) from
|
|
161
219
|
* Beagle public agent allow lists.
|
|
@@ -52,3 +52,19 @@ export function resolveChannelGroupToolsPolicy(params) {
|
|
|
52
52
|
return defaultConfig.tools;
|
|
53
53
|
return undefined;
|
|
54
54
|
}
|
|
55
|
+
export function resolveChannelGroupActivation(params) {
|
|
56
|
+
const { groupConfig, defaultConfig } = resolveChannelGroupPolicy(params);
|
|
57
|
+
// Prefer explicit `activation` field over deprecated `requireMention`
|
|
58
|
+
if (groupConfig?.activation != null)
|
|
59
|
+
return groupConfig.activation;
|
|
60
|
+
if (defaultConfig?.activation != null)
|
|
61
|
+
return defaultConfig.activation;
|
|
62
|
+
// Fall back to legacy requireMention boolean
|
|
63
|
+
if (typeof groupConfig?.requireMention === "boolean") {
|
|
64
|
+
return groupConfig.requireMention ? "mention" : "always";
|
|
65
|
+
}
|
|
66
|
+
if (typeof defaultConfig?.requireMention === "boolean") {
|
|
67
|
+
return defaultConfig.requireMention ? "mention" : "always";
|
|
68
|
+
}
|
|
69
|
+
return undefined;
|
|
70
|
+
}
|
|
@@ -31,6 +31,7 @@ export const WhatsAppAccountSchema = z
|
|
|
31
31
|
groups: z
|
|
32
32
|
.record(z.string(), z
|
|
33
33
|
.object({
|
|
34
|
+
activation: z.enum(["always", "mention", "off"]).optional(),
|
|
34
35
|
requireMention: z.boolean().optional(),
|
|
35
36
|
tools: ToolPolicySchema,
|
|
36
37
|
})
|
|
@@ -93,6 +94,7 @@ export const WhatsAppConfigSchema = z
|
|
|
93
94
|
groups: z
|
|
94
95
|
.record(z.string(), z
|
|
95
96
|
.object({
|
|
97
|
+
activation: z.enum(["always", "mention", "off"]).optional(),
|
|
96
98
|
requireMention: z.boolean().optional(),
|
|
97
99
|
tools: ToolPolicySchema,
|
|
98
100
|
})
|