@rubytech/taskmaster 1.21.1 → 1.21.3

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.
@@ -1,14 +1,13 @@
1
1
  import { PRELOADED_SKILL_AGENTS } from "./types.js";
2
2
  const DEFAULT_AGENTS = ["admin", "public"];
3
3
  /**
4
- * Resolve which agents can access a skill.
5
- * Preloaded skills use the hardcoded map (not overridable).
6
- * User/licensed skills use config, defaulting to all agents.
4
+ * Resolve which agents can access a given skill.
5
+ * - Preloaded skills use the hardcoded map (not overridable by users).
6
+ * - User/licensed skills use `config.skills.entries[name].agents`, defaulting to both agents.
7
7
  */
8
8
  export function resolveSkillAgents(skillName, isPreloaded, config) {
9
- if (isPreloaded) {
9
+ if (isPreloaded)
10
10
  return PRELOADED_SKILL_AGENTS[skillName] ?? DEFAULT_AGENTS;
11
- }
12
11
  const entry = config?.skills?.entries?.[skillName];
13
12
  return entry?.agents ?? DEFAULT_AGENTS;
14
13
  }
@@ -1 +1,25 @@
1
- export {};
1
+ /**
2
+ * Hardcoded agent access for preloaded (bundled) skills.
3
+ * Users cannot override these — they are enforced at prompt build time.
4
+ */
5
+ export const PRELOADED_SKILL_AGENTS = {
6
+ "system-admin": ["admin"],
7
+ "log-review": ["admin"],
8
+ "strategic-proactivity": ["admin"],
9
+ "skill-builder": ["admin"],
10
+ "anthropic": ["admin"],
11
+ "openai": ["admin"],
12
+ "google-ai": ["admin"],
13
+ "tavily": ["admin"],
14
+ "twilio": ["admin"],
15
+ "brevo": ["admin"],
16
+ "stripe": ["admin"],
17
+ "taskmaster": ["admin", "public"],
18
+ "business-assistant": ["admin", "public"],
19
+ "event-management": ["admin", "public"],
20
+ "weather": ["admin", "public"],
21
+ "image-gen": ["admin", "public"],
22
+ "qr-code": ["admin", "public"],
23
+ "sales-closer": ["admin", "public"],
24
+ "zero-to-prototype": ["admin", "public"],
25
+ };
@@ -9,6 +9,7 @@ import { parseFrontmatter, resolveTaskmasterMetadata, resolveSkillInvocationPoli
9
9
  import { resolvePluginSkillDirs } from "./plugin-skills.js";
10
10
  import { hasMarker } from "../../license/skill-pack.js";
11
11
  import { serializeByKey } from "./serialize.js";
12
+ import { resolveSkillAgents } from "./agent-scope.js";
12
13
  const fsp = fs.promises;
13
14
  const skillsLogger = createSubsystemLogger("skills");
14
15
  function escapeXml(str) {
@@ -94,7 +95,21 @@ function debugSkillCommandOnce(messageKey, message, meta) {
94
95
  skillCommandDebugOnce.add(messageKey);
95
96
  skillsLogger.debug(message, meta);
96
97
  }
97
- function filterSkillEntries(entries, config, skillFilter, eligibility) {
98
+ function resolveBundledSkillNamesSet() {
99
+ const dir = resolveBundledSkillsDir();
100
+ if (!dir)
101
+ return new Set();
102
+ try {
103
+ return new Set(fs
104
+ .readdirSync(dir, { withFileTypes: true })
105
+ .filter((d) => d.isDirectory())
106
+ .map((d) => d.name));
107
+ }
108
+ catch {
109
+ return new Set();
110
+ }
111
+ }
112
+ function filterSkillEntries(entries, config, skillFilter, eligibility, agentId) {
98
113
  let filtered = entries.filter((entry) => shouldIncludeSkill({ entry, config, eligibility }));
99
114
  // If skillFilter is provided, only include skills in the filter list.
100
115
  if (skillFilter !== undefined) {
@@ -107,6 +122,15 @@ function filterSkillEntries(entries, config, skillFilter, eligibility) {
107
122
  : [];
108
123
  console.log(`[skills] After filter: ${filtered.map((entry) => entry.skill.name).join(", ")}`);
109
124
  }
125
+ // Agent-level filtering: exclude skills not accessible to this agent.
126
+ if (agentId) {
127
+ const bundledNames = resolveBundledSkillNamesSet();
128
+ filtered = filtered.filter((entry) => {
129
+ const isPreloaded = bundledNames.has(entry.skill.name);
130
+ const allowedAgents = resolveSkillAgents(entry.skill.name, isPreloaded, config);
131
+ return allowedAgents.includes(agentId);
132
+ });
133
+ }
110
134
  return filtered;
111
135
  }
112
136
  const SKILL_COMMAND_MAX_LENGTH = 32;
@@ -214,7 +238,7 @@ function loadSkillEntries(workspaceDir, opts) {
214
238
  }
215
239
  export function buildWorkspaceSkillSnapshot(workspaceDir, opts) {
216
240
  const skillEntries = opts?.entries ?? loadSkillEntries(workspaceDir, opts);
217
- const eligible = filterSkillEntries(skillEntries, opts?.config, opts?.skillFilter, opts?.eligibility);
241
+ const eligible = filterSkillEntries(skillEntries, opts?.config, opts?.skillFilter, opts?.eligibility, opts?.agentId);
218
242
  const promptEntries = eligible.filter((entry) => entry.invocation?.disableModelInvocation !== true);
219
243
  const resolvedSkills = promptEntries.map((entry) => entry.skill);
220
244
  const remoteNote = opts?.eligibility?.remote?.note?.trim();
@@ -233,7 +257,7 @@ export function buildWorkspaceSkillSnapshot(workspaceDir, opts) {
233
257
  }
234
258
  export function buildWorkspaceSkillsPrompt(workspaceDir, opts) {
235
259
  const skillEntries = opts?.entries ?? loadSkillEntries(workspaceDir, opts);
236
- const eligible = filterSkillEntries(skillEntries, opts?.config, opts?.skillFilter, opts?.eligibility);
260
+ const eligible = filterSkillEntries(skillEntries, opts?.config, opts?.skillFilter, opts?.eligibility, opts?.agentId);
237
261
  const promptEntries = eligible.filter((entry) => entry.invocation?.disableModelInvocation !== true);
238
262
  const remoteNote = opts?.eligibility?.remote?.note?.trim();
239
263
  return [remoteNote, formatSkillsForPromptTaskmaster(promptEntries)].filter(Boolean).join("\n");
@@ -354,8 +378,8 @@ export async function syncBundledSkillsToWorkspace(workspaceDir, opts) {
354
378
  }
355
379
  return { synced };
356
380
  }
357
- export function filterWorkspaceSkillEntries(entries, config) {
358
- return filterSkillEntries(entries, config);
381
+ export function filterWorkspaceSkillEntries(entries, config, agentId) {
382
+ return filterSkillEntries(entries, config, undefined, undefined, agentId);
359
383
  }
360
384
  export function buildWorkspaceSkillCommandSpecs(workspaceDir, opts) {
361
385
  const skillEntries = opts?.entries ?? loadSkillEntries(workspaceDir, opts);
@@ -10,6 +10,17 @@ const SkillPackInstallSchema = Type.Object({
10
10
  description: "Absolute path to the .skillpack.json file received as an attachment.",
11
11
  }),
12
12
  });
13
+ /**
14
+ * Resolve the workspace root from the agent workspace dir.
15
+ * Agent tools receive workspaceDir = agent CWD (e.g. ~/taskmaster/agents/admin/)
16
+ * but skills.create writes to the workspace root (e.g. ~/taskmaster/).
17
+ * Strip trailing /agents/<name> to match where skills actually live.
18
+ */
19
+ function resolveSkillsRoot(agentWorkspaceDir) {
20
+ const normalised = agentWorkspaceDir.replace(/\/+$/, "");
21
+ const match = normalised.match(/^(.+)\/agents\/[^/]+$/);
22
+ return match ? match[1] : normalised;
23
+ }
13
24
  export function createSkillPackInstallTool(opts) {
14
25
  return {
15
26
  label: "Skill Pack Install",
@@ -44,8 +55,10 @@ export function createSkillPackInstallTool(opts) {
44
55
  throw new Error("This skill pack is licensed for a different device. " +
45
56
  "It cannot be installed on this device.");
46
57
  }
58
+ // skills.create writes to workspace root, not agent subdir
59
+ const skillsRoot = resolveSkillsRoot(opts.workspaceDir);
47
60
  // Check if all skills in this pack version are already installed
48
- const allAlreadyInstalled = await allSkillsInstalled(opts.workspaceDir, payload.content.skills.map((s) => s.id), payload.pack.id, payload.pack.version);
61
+ const allAlreadyInstalled = await allSkillsInstalled(skillsRoot, payload.content.skills.map((s) => s.id), payload.pack.id, payload.pack.version);
49
62
  if (allAlreadyInstalled) {
50
63
  return jsonResult({
51
64
  ok: true,
@@ -67,8 +80,8 @@ export function createSkillPackInstallTool(opts) {
67
80
  skillContent: entry.skill,
68
81
  ...(references.length > 0 ? { references } : {}),
69
82
  });
70
- // Write marker file for each skill
71
- const skillDir = path.join(opts.workspaceDir, "skills", entry.id);
83
+ // Write marker file use workspace root where skills.create puts the skill
84
+ const skillDir = path.join(skillsRoot, "skills", entry.id);
72
85
  const marker = {
73
86
  packId: payload.pack.id,
74
87
  version: payload.pack.version,
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.21.1",
3
- "commit": "a9d05c02ef2f2cf9eb7cfb55ee76266fea595687",
4
- "builtAt": "2026-03-06T20:52:13.028Z"
2
+ "version": "1.21.3",
3
+ "commit": "1d781522fc324ec5ec42d425cb04e37184af6cd7",
4
+ "builtAt": "2026-03-06T21:08:26.808Z"
5
5
  }
@@ -2145,7 +2145,7 @@ export class MemoryIndexManager {
2145
2145
  throw err;
2146
2146
  }
2147
2147
  const waitMs = Math.min(EMBEDDING_RETRY_MAX_DELAY_MS, Math.round(delayMs * (1 + Math.random() * 0.2)));
2148
- log.warn(`memory embeddings rate limited; retrying in ${waitMs}ms`);
2148
+ log.warn(`memory embeddings error (retrying in ${waitMs}ms): ${message}`);
2149
2149
  await new Promise((resolve) => setTimeout(resolve, waitMs));
2150
2150
  delayMs *= 2;
2151
2151
  attempt += 1;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rubytech/taskmaster",
3
- "version": "1.21.1",
3
+ "version": "1.21.3",
4
4
  "description": "AI-powered business assistant for small businesses",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -961,7 +961,7 @@ Tap the delete icon on a user skill's card. You will be asked to confirm before
961
961
 
962
962
  #### Skill Packs (Licensed Skills)
963
963
 
964
- Your Taskmaster provider can send you additional skills as **skill packs** — signed files that install licensed capabilities onto your device. These are skills built specifically for your business or industry.
964
+ Your Taskmaster provider can send you additional skills as **skill packs** — signed files that install licensed capabilities onto your device. These are skills built specifically for your business or industry. A pack can contain a single skill or a **bundle** of multiple related skills (e.g. all the skills needed for estate agency work).
965
965
 
966
966
  **How to install a skill pack:**
967
967
 
@@ -970,7 +970,9 @@ Your Taskmaster provider can send you additional skills as **skill packs** — s
970
970
  3. Ask your assistant to install it — for example: "Please install this skill pack"
971
971
  4. The assistant verifies the file is genuine and licensed for your device, then installs it
972
972
 
973
- Once installed, the skill appears in the Skills tab with a **Licensed** badge.
973
+ Once installed, each skill appears in the Skills tab with a **Licensed** badge. Bundles install all their skills in one step — you only forward one file.
974
+
975
+ **Sharing your Device ID:** Your provider needs your device ID to create a skill pack for your device. Find it on the **Setup** page → **License** card → tap the info icon → copy the **Device ID** using the copy button. Alternatively, ask your admin assistant: "What's my device ID?"
974
976
 
975
977
  **Updating a skill pack:** Your provider sends you a new version of the file. Follow the same steps — forwarding and asking your assistant to install. The new version replaces the old one automatically.
976
978