@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.
Files changed (52) hide show
  1. package/dist/agents/model-fallback.js +9 -9
  2. package/dist/agents/subagent-announce.js +10 -2
  3. package/dist/agents/tools/cron-tool.js +10 -3
  4. package/dist/agents/tools/skill-read-tool.js +18 -1
  5. package/dist/agents/workspace-migrations.js +48 -0
  6. package/dist/build-info.json +3 -3
  7. package/dist/control-ui/assets/{index-C-AxOsfW.js → index-CkGA_2Sq.js} +494 -423
  8. package/dist/control-ui/assets/index-CkGA_2Sq.js.map +1 -0
  9. package/dist/control-ui/assets/{index-BwiQeiEa.css → index-CxVLKv6r.css} +1 -1
  10. package/dist/control-ui/index.html +2 -2
  11. package/dist/cron/normalize.js +8 -4
  12. package/dist/cron/parse.js +48 -7
  13. package/dist/cron/service/jobs.js +4 -1
  14. package/dist/cron/service/ops.js +16 -0
  15. package/dist/gateway/server-methods/whatsapp-conversations.js +41 -7
  16. package/dist/web/auto-reply/monitor/group-activation.js +1 -1
  17. package/dist/web/inbound/monitor.js +34 -3
  18. package/extensions/.npmignore +1 -0
  19. package/package.json +52 -62
  20. package/scripts/install.sh +0 -0
  21. package/skills/event-management/references/events.md +16 -6
  22. package/skills/whatsapp-business/SKILL.md +47 -0
  23. package/skills/whatsapp-business/references/setup-guide.md +258 -0
  24. package/templates/.DS_Store +0 -0
  25. package/templates/beagle-taxi/.DS_Store +0 -0
  26. package/templates/beagle-taxi/agents/.DS_Store +0 -0
  27. package/templates/beagle-taxi/memory/.DS_Store +0 -0
  28. package/templates/beagle-taxi/skills/.DS_Store +0 -0
  29. package/templates/beagle-zanzibar/.DS_Store +0 -0
  30. package/templates/beagle-zanzibar/agents/.DS_Store +0 -0
  31. package/templates/beagle-zanzibar/memory/.DS_Store +0 -0
  32. package/templates/beagle-zanzibar/skills/.DS_Store +0 -0
  33. package/templates/customer/.DS_Store +0 -0
  34. package/templates/customer/agents/.DS_Store +0 -0
  35. package/templates/education-hero/.DS_Store +0 -0
  36. package/templates/education-hero/agents/.DS_Store +0 -0
  37. package/templates/education-hero/agents/admin/.DS_Store +0 -0
  38. package/templates/education-hero/skills/.DS_Store +0 -0
  39. package/templates/education-hero/skills/education-hero/.DS_Store +0 -0
  40. package/templates/maxy/.DS_Store +0 -0
  41. package/templates/maxy/.gitignore +1 -0
  42. package/templates/maxy/agents/.DS_Store +0 -0
  43. package/templates/maxy/agents/admin/.DS_Store +0 -0
  44. package/templates/maxy/memory/.DS_Store +0 -0
  45. package/templates/maxy/skills/.DS_Store +0 -0
  46. package/templates/real-agent/.DS_Store +0 -0
  47. package/templates/real-agent/skills/.DS_Store +0 -0
  48. package/templates/taskmaster/.DS_Store +0 -0
  49. package/templates/taskmaster/.gitignore +1 -0
  50. package/templates/taskmaster/agents/public/AGENTS.md +16 -0
  51. package/templates/taskmaster/skills/.DS_Store +0 -0
  52. 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: true,
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: true,
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": <unix-ms-timestamp> }
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 job = normalizeCronJobCreate(params.job) ?? params.job;
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
- throw new Error("Relative path requires skill_dir. Provide an absolute path or include skill_dir.");
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.
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.29.2",
3
- "commit": "d2161d7c3053b10edd3f8d588413a6c2cde3a8e7",
4
- "builtAt": "2026-03-08T07:35:10.314Z"
2
+ "version": "1.30.1",
3
+ "commit": "014eac6ca905695d971d1b75e2a015abd85f9be1",
4
+ "builtAt": "2026-03-08T17:31:05.664Z"
5
5
  }