@rubytech/taskmaster 1.29.2 → 1.30.1
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/model-fallback.js +9 -9
- package/dist/agents/subagent-announce.js +10 -2
- package/dist/agents/tools/cron-tool.js +10 -3
- package/dist/agents/tools/skill-read-tool.js +18 -1
- package/dist/agents/workspace-migrations.js +48 -0
- package/dist/build-info.json +3 -3
- package/dist/control-ui/assets/{index-C-AxOsfW.js → index-CkGA_2Sq.js} +494 -423
- package/dist/control-ui/assets/index-CkGA_2Sq.js.map +1 -0
- package/dist/control-ui/assets/{index-BwiQeiEa.css → index-CxVLKv6r.css} +1 -1
- package/dist/control-ui/index.html +2 -2
- package/dist/cron/normalize.js +8 -4
- package/dist/cron/parse.js +48 -7
- package/dist/cron/service/jobs.js +4 -1
- package/dist/cron/service/ops.js +16 -0
- package/dist/gateway/server-methods/whatsapp-conversations.js +41 -7
- package/dist/web/auto-reply/monitor/group-activation.js +1 -1
- package/dist/web/inbound/monitor.js +34 -3
- package/extensions/.npmignore +1 -0
- package/package.json +52 -62
- package/scripts/install.sh +0 -0
- package/skills/event-management/references/events.md +16 -6
- package/skills/whatsapp-business/SKILL.md +47 -0
- package/skills/whatsapp-business/references/setup-guide.md +258 -0
- package/templates/.DS_Store +0 -0
- package/templates/beagle-taxi/.DS_Store +0 -0
- package/templates/beagle-taxi/agents/.DS_Store +0 -0
- package/templates/beagle-taxi/memory/.DS_Store +0 -0
- package/templates/beagle-taxi/skills/.DS_Store +0 -0
- package/templates/beagle-zanzibar/.DS_Store +0 -0
- package/templates/beagle-zanzibar/agents/.DS_Store +0 -0
- package/templates/beagle-zanzibar/memory/.DS_Store +0 -0
- package/templates/beagle-zanzibar/skills/.DS_Store +0 -0
- package/templates/customer/.DS_Store +0 -0
- package/templates/customer/agents/.DS_Store +0 -0
- package/templates/education-hero/.DS_Store +0 -0
- package/templates/education-hero/agents/.DS_Store +0 -0
- package/templates/education-hero/agents/admin/.DS_Store +0 -0
- package/templates/education-hero/skills/.DS_Store +0 -0
- package/templates/education-hero/skills/education-hero/.DS_Store +0 -0
- package/templates/maxy/.DS_Store +0 -0
- package/templates/maxy/.gitignore +1 -0
- package/templates/maxy/agents/.DS_Store +0 -0
- package/templates/maxy/agents/admin/.DS_Store +0 -0
- package/templates/maxy/memory/.DS_Store +0 -0
- package/templates/maxy/skills/.DS_Store +0 -0
- package/templates/real-agent/.DS_Store +0 -0
- package/templates/real-agent/skills/.DS_Store +0 -0
- package/templates/taskmaster/.DS_Store +0 -0
- package/templates/taskmaster/.gitignore +1 -0
- package/templates/taskmaster/agents/public/AGENTS.md +16 -0
- package/templates/taskmaster/skills/.DS_Store +0 -0
- package/dist/control-ui/assets/index-C-AxOsfW.js.map +0 -1
|
@@ -150,6 +150,15 @@ export async function runWithModelFallback(params) {
|
|
|
150
150
|
const candidate = candidates[i];
|
|
151
151
|
try {
|
|
152
152
|
const result = await params.run(candidate.provider, candidate.model);
|
|
153
|
+
// A fallback actually recovered — notify the UI with a clean message.
|
|
154
|
+
if (attempts.length > 0) {
|
|
155
|
+
const primary = attempts[0];
|
|
156
|
+
emitInfraAlertEvent({
|
|
157
|
+
category: "model-fallback",
|
|
158
|
+
message: `Primary model unavailable — using ${candidate.provider}/${candidate.model} instead.`,
|
|
159
|
+
});
|
|
160
|
+
log.info(`[model-fallback] recovered: ${primary.provider}/${primary.model} failed, using ${candidate.provider}/${candidate.model}`);
|
|
161
|
+
}
|
|
153
162
|
return {
|
|
154
163
|
result,
|
|
155
164
|
provider: candidate.provider,
|
|
@@ -182,15 +191,6 @@ export async function runWithModelFallback(params) {
|
|
|
182
191
|
: " — no more fallback candidates");
|
|
183
192
|
log.warn(fallbackMsg);
|
|
184
193
|
console.warn(fallbackMsg);
|
|
185
|
-
if (i === 0) {
|
|
186
|
-
emitInfraAlertEvent({
|
|
187
|
-
category: "model-fallback",
|
|
188
|
-
message: `${candidate.provider}/${candidate.model} failed (${described.reason ?? "unknown"}): ${described.message}` +
|
|
189
|
-
(i + 1 < candidates.length
|
|
190
|
-
? `. Falling back to ${candidates[i + 1].provider}/${candidates[i + 1].model}.`
|
|
191
|
-
: ". No fallback available."),
|
|
192
|
-
});
|
|
193
|
-
}
|
|
194
194
|
await params.onError?.({
|
|
195
195
|
provider: candidate.provider,
|
|
196
196
|
model: candidate.model,
|
|
@@ -77,6 +77,10 @@ function resolveAnnounceOrigin(entry, requesterOrigin) {
|
|
|
77
77
|
async function sendAnnounce(item) {
|
|
78
78
|
const origin = item.origin;
|
|
79
79
|
const threadId = origin?.threadId != null && origin.threadId !== "" ? String(origin.threadId) : undefined;
|
|
80
|
+
// Only attempt external delivery when we have a concrete recipient.
|
|
81
|
+
// Channels like WhatsApp reject delivery without a target (E.164 / group JID).
|
|
82
|
+
// Omitting `deliver` still stores the message in the session transcript.
|
|
83
|
+
const canDeliver = Boolean(origin?.to);
|
|
80
84
|
await callGateway({
|
|
81
85
|
method: "agent",
|
|
82
86
|
params: {
|
|
@@ -86,7 +90,7 @@ async function sendAnnounce(item) {
|
|
|
86
90
|
accountId: origin?.accountId,
|
|
87
91
|
to: origin?.to,
|
|
88
92
|
threadId,
|
|
89
|
-
deliver:
|
|
93
|
+
deliver: canDeliver,
|
|
90
94
|
idempotencyKey: crypto.randomUUID(),
|
|
91
95
|
},
|
|
92
96
|
expectFinal: true,
|
|
@@ -337,12 +341,16 @@ export async function runSubagentAnnounceFlow(params) {
|
|
|
337
341
|
const { entry } = loadRequesterSessionEntry(params.requesterSessionKey);
|
|
338
342
|
directOrigin = deliveryContextFromSession(entry);
|
|
339
343
|
}
|
|
344
|
+
// Only attempt external delivery when we have a concrete recipient.
|
|
345
|
+
// Cron-spawned subagents inherit the account's channel (e.g. whatsapp)
|
|
346
|
+
// but have no `to` — delivery would fail with "requires target".
|
|
347
|
+
const canDeliver = Boolean(directOrigin?.to);
|
|
340
348
|
await callGateway({
|
|
341
349
|
method: "agent",
|
|
342
350
|
params: {
|
|
343
351
|
sessionKey: params.requesterSessionKey,
|
|
344
352
|
message: triggerMessage,
|
|
345
|
-
deliver:
|
|
353
|
+
deliver: canDeliver,
|
|
346
354
|
channel: directOrigin?.channel,
|
|
347
355
|
accountId: directOrigin?.accountId,
|
|
348
356
|
to: directOrigin?.to,
|
|
@@ -138,7 +138,12 @@ EVENT SCHEMA (for add action):
|
|
|
138
138
|
|
|
139
139
|
SCHEDULE TYPES (schedule.kind):
|
|
140
140
|
- "at": One-shot at absolute time
|
|
141
|
-
{ "kind": "at", "atMs": <
|
|
141
|
+
{ "kind": "at", "atMs": "<ISO-datetime-string>" }
|
|
142
|
+
PREFERRED: pass atMs as an ISO 8601 string, e.g. "2026-03-11T09:00:00".
|
|
143
|
+
The system interprets timezone-naive strings in the user's configured timezone.
|
|
144
|
+
You may also pass a numeric unix-ms timestamp, but ISO strings are less error-prone.
|
|
145
|
+
IMPORTANT: atMs must be in the future. Jobs with past atMs are rejected.
|
|
146
|
+
Always call current_time first to confirm the current date and year before scheduling.
|
|
142
147
|
- "every": Recurring interval
|
|
143
148
|
{ "kind": "every", "everyMs": <interval-ms>, "anchorMs": <optional-start-ms> }
|
|
144
149
|
- "cron": Cron expression
|
|
@@ -184,9 +189,11 @@ Use jobId as the canonical identifier; id is accepted for compatibility. Use con
|
|
|
184
189
|
if (!params.job || typeof params.job !== "object") {
|
|
185
190
|
throw new Error("job required");
|
|
186
191
|
}
|
|
187
|
-
const
|
|
192
|
+
const cfg = loadConfig();
|
|
193
|
+
const userTimezone = cfg.agents?.defaults?.userTimezone;
|
|
194
|
+
const job = normalizeCronJobCreate(params.job, { userTimezone: userTimezone || undefined }) ??
|
|
195
|
+
params.job;
|
|
188
196
|
if (job && typeof job === "object" && !("agentId" in job)) {
|
|
189
|
-
const cfg = loadConfig();
|
|
190
197
|
const agentId = opts?.agentSessionKey
|
|
191
198
|
? resolveSessionAgentId({ sessionKey: opts.agentSessionKey, config: cfg })
|
|
192
199
|
: undefined;
|
|
@@ -37,7 +37,24 @@ export function createSkillReadTool(opts) {
|
|
|
37
37
|
targetPath = path.resolve(skillDir, rawPath);
|
|
38
38
|
}
|
|
39
39
|
else {
|
|
40
|
-
|
|
40
|
+
// Fallback: search allowedDirs for the relative path.
|
|
41
|
+
// Sub-agents may omit skill_dir; resolve against known skill dirs.
|
|
42
|
+
let found;
|
|
43
|
+
for (const dir of allowedDirs) {
|
|
44
|
+
const candidate = path.resolve(dir, rawPath);
|
|
45
|
+
try {
|
|
46
|
+
await fs.access(candidate);
|
|
47
|
+
found = candidate;
|
|
48
|
+
break;
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
// not in this dir, try next
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
if (!found) {
|
|
55
|
+
throw new Error(`Relative path "${rawPath}" not found in any skill directory. Provide an absolute path or include skill_dir.`);
|
|
56
|
+
}
|
|
57
|
+
targetPath = found;
|
|
41
58
|
}
|
|
42
59
|
// Security: resolve symlinks on the target and check containment
|
|
43
60
|
let realTarget;
|
|
@@ -709,6 +709,53 @@ async function patchBeagleAdminDriverContactTone(_agentsPath, content) {
|
|
|
709
709
|
return content.replace(ADMIN_DRIVER_CONTACT_OLD, ADMIN_DRIVER_CONTACT_NEW);
|
|
710
710
|
}
|
|
711
711
|
// ---------------------------------------------------------------------------
|
|
712
|
+
// Migration: Public Agent Write Permissions (v1.30)
|
|
713
|
+
// ---------------------------------------------------------------------------
|
|
714
|
+
//
|
|
715
|
+
// The public agent's memory writes are scope-restricted, but its instructions
|
|
716
|
+
// never stated which paths are writable. This caused 20+ retries per session
|
|
717
|
+
// as the agent tried to write to restricted paths (e.g. memory/public/).
|
|
718
|
+
const WRITE_PERMISSIONS_MARKER = "## Write Permissions";
|
|
719
|
+
const PUBLIC_WRITE_PERMISSIONS_SECTION = `## Write Permissions
|
|
720
|
+
|
|
721
|
+
Your memory writes are restricted by scope. Only these paths are writable:
|
|
722
|
+
|
|
723
|
+
- \`memory/users/{phone}/**\` — user profiles and data (DM conversations only)
|
|
724
|
+
- \`memory/groups/{groupId}/**\` — group member profiles and data (group conversations only)
|
|
725
|
+
- \`memory/shared/events/**\` — shared event data
|
|
726
|
+
|
|
727
|
+
Everything else is **read-only**. In particular:
|
|
728
|
+
- \`memory/public/\` — read-only (knowledge base, product info)
|
|
729
|
+
- \`memory/shared/\` outside \`events/\` — read-only
|
|
730
|
+
|
|
731
|
+
If a write fails with "scope restricted", do not retry with the same or similar path. The restriction is permanent. Store the data under an allowed path instead.`;
|
|
732
|
+
function hasWritePermissionsSection(content) {
|
|
733
|
+
return content.includes(WRITE_PERMISSIONS_MARKER);
|
|
734
|
+
}
|
|
735
|
+
/**
|
|
736
|
+
* Find insertion point before "## Memory Usage" (with or without emoji prefix).
|
|
737
|
+
*/
|
|
738
|
+
function findMemoryUsageInsertPoint(content) {
|
|
739
|
+
// Match both "## 🔍 Memory Usage" and "## Memory Usage"
|
|
740
|
+
const emojiIdx = content.indexOf("## 🔍 Memory Usage");
|
|
741
|
+
if (emojiIdx !== -1)
|
|
742
|
+
return emojiIdx;
|
|
743
|
+
const plainIdx = content.indexOf("## Memory Usage");
|
|
744
|
+
if (plainIdx !== -1)
|
|
745
|
+
return plainIdx;
|
|
746
|
+
return -1;
|
|
747
|
+
}
|
|
748
|
+
async function patchPublicWritePermissions(_agentsPath, content) {
|
|
749
|
+
if (!isPublicAgent(content))
|
|
750
|
+
return null;
|
|
751
|
+
if (hasWritePermissionsSection(content))
|
|
752
|
+
return null;
|
|
753
|
+
// Only apply to agents with a Memory Usage section (Taskmaster public agent)
|
|
754
|
+
if (!content.includes("Memory Usage"))
|
|
755
|
+
return null;
|
|
756
|
+
return insertSection(content, PUBLIC_WRITE_PERMISSIONS_SECTION, findMemoryUsageInsertPoint);
|
|
757
|
+
}
|
|
758
|
+
// ---------------------------------------------------------------------------
|
|
712
759
|
// Migration: Sub-Agent Delegation (v1.24)
|
|
713
760
|
// ---------------------------------------------------------------------------
|
|
714
761
|
const DELEGATION_SECTION = `## Delegation — Sub-Agents
|
|
@@ -787,6 +834,7 @@ const MIGRATIONS = [
|
|
|
787
834
|
{ name: "beagle-admin-driver-reply-explicit", apply: patchBeagleAdminDriverReplyExplicit },
|
|
788
835
|
{ name: "beagle-admin-driver-contact-tone", apply: patchBeagleAdminDriverContactTone },
|
|
789
836
|
{ name: "delegation-section", apply: patchDelegationSection },
|
|
837
|
+
{ name: "public-write-permissions", apply: patchPublicWritePermissions },
|
|
790
838
|
];
|
|
791
839
|
/**
|
|
792
840
|
* Run all workspace migrations for every configured agent.
|
package/dist/build-info.json
CHANGED