@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.
- package/dist/agents/skills/agent-scope.js +4 -5
- package/dist/agents/skills/types.js +25 -1
- package/dist/agents/skills/workspace.js +29 -5
- package/dist/agents/tools/skill-pack-install-tool.js +16 -3
- package/dist/build-info.json +3 -3
- package/dist/memory/manager.js +1 -1
- package/package.json +1 -1
- package/taskmaster-docs/USER-GUIDE.md +4 -2
|
@@ -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
|
|
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
|
-
|
|
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
|
|
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(
|
|
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
|
|
71
|
-
const skillDir = path.join(
|
|
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,
|
package/dist/build-info.json
CHANGED
package/dist/memory/manager.js
CHANGED
|
@@ -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
|
|
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
|
@@ -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,
|
|
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
|
|