@rubytech/taskmaster 1.42.0 → 1.42.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 (41) hide show
  1. package/dist/agents/context.js +2 -0
  2. package/dist/agents/pi-embedded-runner/compact.js +1 -0
  3. package/dist/agents/pi-embedded-runner/run/attempt.js +1 -0
  4. package/dist/agents/pi-embedded-runner/system-prompt.js +1 -0
  5. package/dist/agents/skills/frontmatter.js +85 -1
  6. package/dist/agents/skills/workspace.js +23 -2
  7. package/dist/agents/skills-status.js +2 -2
  8. package/dist/agents/system-prompt.js +43 -30
  9. package/dist/agents/tool-policy.js +68 -4
  10. package/dist/agents/tools/access-manage-tool.js +110 -0
  11. package/dist/agents/tools/account-manage-tool.js +78 -0
  12. package/dist/agents/tools/brand-settings-tool.js +53 -24
  13. package/dist/agents/tools/file-manage-tool.js +193 -0
  14. package/dist/agents/tools/license-manage-tool.js +50 -0
  15. package/dist/agents/tools/skill-manage-tool.js +16 -3
  16. package/dist/auto-reply/reply/commands-context-report.js +2 -1
  17. package/dist/browser/chrome.js +11 -0
  18. package/dist/build-info.json +3 -3
  19. package/dist/cli/skills-cli.js +4 -9
  20. package/dist/commands/onboard-skills.js +1 -1
  21. package/dist/config/zod-schema.js +5 -1
  22. package/dist/control-ui/assets/index-CAu2PL0O.css +1 -0
  23. package/dist/control-ui/assets/{index-Cl91wvkO.js → index-c6Rca5oP.js} +691 -542
  24. package/dist/control-ui/assets/index-c6Rca5oP.js.map +1 -0
  25. package/dist/control-ui/index.html +3 -2
  26. package/dist/gateway/protocol/index.js +3 -2
  27. package/dist/gateway/protocol/schema/agents-models-skills.js +6 -1
  28. package/dist/gateway/protocol/schema/tools.js +8 -0
  29. package/dist/gateway/server-methods/sessions.js +3 -1
  30. package/dist/gateway/server-methods/skills.js +31 -2
  31. package/dist/gateway/server-methods/tools.js +7 -0
  32. package/dist/gateway/server-methods.js +3 -0
  33. package/dist/gateway/session-utils.js +5 -1
  34. package/dist/media-understanding/apply.js +18 -4
  35. package/dist/web/auto-reply/monitor/process-message.js +1 -0
  36. package/dist/web/inbound/monitor.js +4 -0
  37. package/package.json +1 -1
  38. package/skills/skill-builder/SKILL.md +18 -4
  39. package/skills/skill-builder/references/lean-pattern.md +13 -3
  40. package/dist/control-ui/assets/index-Cl91wvkO.js.map +0 -1
  41. package/dist/control-ui/assets/index-Dd2cHcuh.css +0 -1
@@ -6,10 +6,11 @@
6
6
  <title>Taskmaster Control</title>
7
7
  <meta name="color-scheme" content="dark light" />
8
8
  <link rel="icon" type="image/png" href="./favicon.png?v=2" />
9
- <script type="module" crossorigin src="./assets/index-Cl91wvkO.js"></script>
10
- <link rel="stylesheet" crossorigin href="./assets/index-Dd2cHcuh.css">
9
+ <script type="module" crossorigin src="./assets/index-c6Rca5oP.js"></script>
10
+ <link rel="stylesheet" crossorigin href="./assets/index-CAu2PL0O.css">
11
11
  </head>
12
12
  <body>
13
13
  <taskmaster-app></taskmaster-app>
14
+ <script src="https://unpkg.com/lucide@latest" defer></script>
14
15
  </body>
15
16
  </html>
@@ -1,5 +1,5 @@
1
1
  import AjvPkg from "ajv";
2
- import { AgentEventSchema, AgentIdentityParamsSchema, AgentIdentityResultSchema, AgentParamsSchema, AgentSummarySchema, AgentsListParamsSchema, AgentsListResultSchema, AgentWaitParamsSchema, ChannelsLogoutParamsSchema, ChannelsProbeTokenParamsSchema, ChannelsStatusParamsSchema, ChannelsStatusResultSchema, ChatAbortParamsSchema, ChatEventSchema, ChatHistoryParamsSchema, ChatInjectParamsSchema, ChatSendParamsSchema, ConfigApplyParamsSchema, ConfigGetParamsSchema, ConfigPatchParamsSchema, ConfigSchemaParamsSchema, ConfigSchemaResponseSchema, ConfigSetParamsSchema, ConnectParamsSchema, CronAddParamsSchema, CronJobSchema, CronListParamsSchema, CronRemoveParamsSchema, CronRunParamsSchema, CronRunsParamsSchema, CronStatusParamsSchema, CronUpdateParamsSchema, DevicePairApproveParamsSchema, DevicePairListParamsSchema, DevicePairRejectParamsSchema, DeviceTokenRevokeParamsSchema, DeviceTokenRotateParamsSchema, ExecApprovalsGetParamsSchema, ExecApprovalsNodeGetParamsSchema, ExecApprovalsNodeSetParamsSchema, ExecApprovalsSetParamsSchema, ExecApprovalRequestParamsSchema, ExecApprovalResolveParamsSchema, ErrorCodes, ErrorShapeSchema, EventFrameSchema, errorShape, GatewayFrameSchema, HelloOkSchema, LogsTailParamsSchema, LogsTailResultSchema, SessionsTranscriptParamsSchema, SessionsTranscriptResultSchema, ModelsListParamsSchema, NodeDescribeParamsSchema, NodeEventParamsSchema, NodeInvokeParamsSchema, NodeInvokeResultParamsSchema, NodeListParamsSchema, NodePairApproveParamsSchema, NodePairListParamsSchema, NodePairRejectParamsSchema, NodePairRequestParamsSchema, NodePairVerifyParamsSchema, NodeRenameParamsSchema, PollParamsSchema, PROTOCOL_VERSION, PresenceEntrySchema, ProtocolSchemas, RequestFrameSchema, ResponseFrameSchema, SendParamsSchema, SessionsCompactParamsSchema, SessionsDeleteParamsSchema, SessionsListParamsSchema, SessionsPatchParamsSchema, SessionsPreviewParamsSchema, SessionsResetParamsSchema, SessionsResolveParamsSchema, ShutdownEventSchema, SkillsBinsParamsSchema, SkillsCreateParamsSchema, SkillsDeleteParamsSchema, SkillsDeleteDraftParamsSchema, SkillsDraftsParamsSchema, SkillsSaveDraftParamsSchema, SkillsInstallParamsSchema, SkillsReadParamsSchema, SkillsStatusParamsSchema, SkillsUpdateParamsSchema, SnapshotSchema, StateVersionSchema, TalkModeParamsSchema, TickEventSchema, UpdateRunParamsSchema, WakeParamsSchema, WebLoginStartParamsSchema, WebLoginWaitParamsSchema, WizardCancelParamsSchema, WizardNextParamsSchema, WizardNextResultSchema, WhatsAppConversationsParamsSchema, WhatsAppGroupInfoParamsSchema, WhatsAppMessagesParamsSchema, WhatsAppSendMessageParamsSchema, WhatsAppSetActivationParamsSchema, WizardStartParamsSchema, WizardStartResultSchema, WizardStatusParamsSchema, WizardStatusResultSchema, WizardStepSchema, } from "./schema.js";
2
+ import { AgentEventSchema, AgentIdentityParamsSchema, AgentIdentityResultSchema, AgentParamsSchema, AgentSummarySchema, AgentsListParamsSchema, AgentsListResultSchema, AgentWaitParamsSchema, ChannelsLogoutParamsSchema, ChannelsProbeTokenParamsSchema, ChannelsStatusParamsSchema, ChannelsStatusResultSchema, ChatAbortParamsSchema, ChatEventSchema, ChatHistoryParamsSchema, ChatInjectParamsSchema, ChatSendParamsSchema, ConfigApplyParamsSchema, ConfigGetParamsSchema, ConfigPatchParamsSchema, ConfigSchemaParamsSchema, ConfigSchemaResponseSchema, ConfigSetParamsSchema, ConnectParamsSchema, CronAddParamsSchema, CronJobSchema, CronListParamsSchema, CronRemoveParamsSchema, CronRunParamsSchema, CronRunsParamsSchema, CronStatusParamsSchema, CronUpdateParamsSchema, DevicePairApproveParamsSchema, DevicePairListParamsSchema, DevicePairRejectParamsSchema, DeviceTokenRevokeParamsSchema, DeviceTokenRotateParamsSchema, ExecApprovalsGetParamsSchema, ExecApprovalsNodeGetParamsSchema, ExecApprovalsNodeSetParamsSchema, ExecApprovalsSetParamsSchema, ExecApprovalRequestParamsSchema, ExecApprovalResolveParamsSchema, ErrorCodes, ErrorShapeSchema, EventFrameSchema, errorShape, GatewayFrameSchema, HelloOkSchema, LogsTailParamsSchema, LogsTailResultSchema, SessionsTranscriptParamsSchema, SessionsTranscriptResultSchema, ModelsListParamsSchema, NodeDescribeParamsSchema, NodeEventParamsSchema, NodeInvokeParamsSchema, NodeInvokeResultParamsSchema, NodeListParamsSchema, NodePairApproveParamsSchema, NodePairListParamsSchema, NodePairRejectParamsSchema, NodePairRequestParamsSchema, NodePairVerifyParamsSchema, NodeRenameParamsSchema, PollParamsSchema, PROTOCOL_VERSION, PresenceEntrySchema, ProtocolSchemas, RequestFrameSchema, ResponseFrameSchema, SendParamsSchema, SessionsCompactParamsSchema, SessionsDeleteParamsSchema, SessionsListParamsSchema, SessionsPatchParamsSchema, SessionsPreviewParamsSchema, SessionsResetParamsSchema, SessionsResolveParamsSchema, ShutdownEventSchema, SkillsBinsParamsSchema, SkillsCreateParamsSchema, SkillsDeleteParamsSchema, SkillsDeleteDraftParamsSchema, SkillsDraftsParamsSchema, SkillsSaveDraftParamsSchema, SkillsInstallParamsSchema, SkillsPatchParamsSchema, SkillsReadParamsSchema, SkillsStatusParamsSchema, SkillsUpdateParamsSchema, SnapshotSchema, StateVersionSchema, TalkModeParamsSchema, TickEventSchema, UpdateRunParamsSchema, WakeParamsSchema, WebLoginStartParamsSchema, WebLoginWaitParamsSchema, WizardCancelParamsSchema, WizardNextParamsSchema, WizardNextResultSchema, WhatsAppConversationsParamsSchema, WhatsAppGroupInfoParamsSchema, WhatsAppMessagesParamsSchema, WhatsAppSendMessageParamsSchema, WhatsAppSetActivationParamsSchema, WizardStartParamsSchema, WizardStartResultSchema, WizardStatusParamsSchema, WizardStatusResultSchema, WizardStepSchema, } from "./schema.js";
3
3
  const ajv = new AjvPkg({
4
4
  allErrors: true,
5
5
  strict: false,
@@ -52,6 +52,7 @@ export const validateSkillsStatusParams = ajv.compile(SkillsStatusParamsSchema);
52
52
  export const validateSkillsBinsParams = ajv.compile(SkillsBinsParamsSchema);
53
53
  export const validateSkillsInstallParams = ajv.compile(SkillsInstallParamsSchema);
54
54
  export const validateSkillsUpdateParams = ajv.compile(SkillsUpdateParamsSchema);
55
+ export const validateSkillsPatchParams = ajv.compile(SkillsPatchParamsSchema);
55
56
  export const validateSkillsReadParams = ajv.compile(SkillsReadParamsSchema);
56
57
  export const validateSkillsCreateParams = ajv.compile(SkillsCreateParamsSchema);
57
58
  export const validateSkillsDeleteParams = ajv.compile(SkillsDeleteParamsSchema);
@@ -119,4 +120,4 @@ export function formatValidationErrors(errors) {
119
120
  }
120
121
  return unique.join("; ");
121
122
  }
122
- export { ConnectParamsSchema, HelloOkSchema, RequestFrameSchema, ResponseFrameSchema, EventFrameSchema, GatewayFrameSchema, PresenceEntrySchema, SnapshotSchema, ErrorShapeSchema, StateVersionSchema, AgentEventSchema, ChatEventSchema, SendParamsSchema, PollParamsSchema, AgentParamsSchema, AgentIdentityParamsSchema, AgentIdentityResultSchema, WakeParamsSchema, NodePairRequestParamsSchema, NodePairListParamsSchema, NodePairApproveParamsSchema, NodePairRejectParamsSchema, NodePairVerifyParamsSchema, NodeListParamsSchema, NodeInvokeParamsSchema, SessionsListParamsSchema, SessionsPreviewParamsSchema, SessionsPatchParamsSchema, SessionsResetParamsSchema, SessionsDeleteParamsSchema, SessionsCompactParamsSchema, ConfigGetParamsSchema, ConfigSetParamsSchema, ConfigApplyParamsSchema, ConfigPatchParamsSchema, ConfigSchemaParamsSchema, ConfigSchemaResponseSchema, WizardStartParamsSchema, WizardNextParamsSchema, WizardCancelParamsSchema, WizardStatusParamsSchema, WizardStepSchema, WizardNextResultSchema, WizardStartResultSchema, WizardStatusResultSchema, ChannelsStatusParamsSchema, ChannelsStatusResultSchema, ChannelsLogoutParamsSchema, ChannelsProbeTokenParamsSchema, WebLoginStartParamsSchema, WebLoginWaitParamsSchema, WhatsAppConversationsParamsSchema, WhatsAppGroupInfoParamsSchema, WhatsAppMessagesParamsSchema, WhatsAppSendMessageParamsSchema, WhatsAppSetActivationParamsSchema, AgentSummarySchema, AgentsListParamsSchema, AgentsListResultSchema, ModelsListParamsSchema, SkillsStatusParamsSchema, SkillsInstallParamsSchema, SkillsUpdateParamsSchema, CronJobSchema, CronListParamsSchema, CronStatusParamsSchema, CronAddParamsSchema, CronUpdateParamsSchema, CronRemoveParamsSchema, CronRunParamsSchema, CronRunsParamsSchema, LogsTailParamsSchema, LogsTailResultSchema, SessionsTranscriptParamsSchema, SessionsTranscriptResultSchema, ChatHistoryParamsSchema, ChatSendParamsSchema, ChatInjectParamsSchema, UpdateRunParamsSchema, TickEventSchema, ShutdownEventSchema, ProtocolSchemas, PROTOCOL_VERSION, ErrorCodes, errorShape, };
123
+ export { ConnectParamsSchema, HelloOkSchema, RequestFrameSchema, ResponseFrameSchema, EventFrameSchema, GatewayFrameSchema, PresenceEntrySchema, SnapshotSchema, ErrorShapeSchema, StateVersionSchema, AgentEventSchema, ChatEventSchema, SendParamsSchema, PollParamsSchema, AgentParamsSchema, AgentIdentityParamsSchema, AgentIdentityResultSchema, WakeParamsSchema, NodePairRequestParamsSchema, NodePairListParamsSchema, NodePairApproveParamsSchema, NodePairRejectParamsSchema, NodePairVerifyParamsSchema, NodeListParamsSchema, NodeInvokeParamsSchema, SessionsListParamsSchema, SessionsPreviewParamsSchema, SessionsPatchParamsSchema, SessionsResetParamsSchema, SessionsDeleteParamsSchema, SessionsCompactParamsSchema, ConfigGetParamsSchema, ConfigSetParamsSchema, ConfigApplyParamsSchema, ConfigPatchParamsSchema, ConfigSchemaParamsSchema, ConfigSchemaResponseSchema, WizardStartParamsSchema, WizardNextParamsSchema, WizardCancelParamsSchema, WizardStatusParamsSchema, WizardStepSchema, WizardNextResultSchema, WizardStartResultSchema, WizardStatusResultSchema, ChannelsStatusParamsSchema, ChannelsStatusResultSchema, ChannelsLogoutParamsSchema, ChannelsProbeTokenParamsSchema, WebLoginStartParamsSchema, WebLoginWaitParamsSchema, WhatsAppConversationsParamsSchema, WhatsAppGroupInfoParamsSchema, WhatsAppMessagesParamsSchema, WhatsAppSendMessageParamsSchema, WhatsAppSetActivationParamsSchema, AgentSummarySchema, AgentsListParamsSchema, AgentsListResultSchema, ModelsListParamsSchema, SkillsStatusParamsSchema, SkillsInstallParamsSchema, SkillsPatchParamsSchema, SkillsUpdateParamsSchema, CronJobSchema, CronListParamsSchema, CronStatusParamsSchema, CronAddParamsSchema, CronUpdateParamsSchema, CronRemoveParamsSchema, CronRunParamsSchema, CronRunsParamsSchema, LogsTailParamsSchema, LogsTailResultSchema, SessionsTranscriptParamsSchema, SessionsTranscriptResultSchema, ChatHistoryParamsSchema, ChatSendParamsSchema, ChatInjectParamsSchema, UpdateRunParamsSchema, TickEventSchema, ShutdownEventSchema, ProtocolSchemas, PROTOCOL_VERSION, ErrorCodes, errorShape, };
@@ -44,10 +44,15 @@ export const SkillsUpdateParamsSchema = Type.Object({
44
44
  enabled: Type.Optional(Type.Boolean()),
45
45
  apiKey: Type.Optional(Type.String()),
46
46
  env: Type.Optional(Type.Record(NonEmptyString, Type.String())),
47
- agents: Type.Optional(Type.Array(Type.Union([Type.Literal("admin"), Type.Literal("public")]), {
47
+ agents: Type.Optional(Type.Array(Type.Union([Type.Literal("admin"), Type.Literal("public"), Type.Literal("subagent")]), {
48
48
  minItems: 1,
49
49
  })),
50
50
  }, { additionalProperties: false });
51
+ export const SkillsPatchParamsSchema = Type.Object({
52
+ name: Type.String({ minLength: 1 }),
53
+ icon: Type.Optional(Type.String()),
54
+ tools: Type.Optional(Type.Array(Type.String())),
55
+ });
51
56
  export const SkillsReadParamsSchema = Type.Object({
52
57
  name: NonEmptyString,
53
58
  }, { additionalProperties: false });
@@ -0,0 +1,8 @@
1
+ import { Type } from "@sinclair/typebox";
2
+ export const ToolsListParamsSchema = Type.Object({});
3
+ export const ToolsListResponseSchema = Type.Object({
4
+ tools: Type.Array(Type.Object({
5
+ name: Type.String(),
6
+ description: Type.String(),
7
+ })),
8
+ });
@@ -1,6 +1,7 @@
1
1
  import { randomUUID } from "node:crypto";
2
2
  import fs from "node:fs";
3
3
  import { abortEmbeddedPiRun, waitForEmbeddedPiRunEnd } from "../../agents/pi-embedded.js";
4
+ import { modelCacheReady } from "../../agents/context.js";
4
5
  import { stopSubagentsForRequester } from "../../auto-reply/reply/abort.js";
5
6
  import { clearSessionQueues } from "../../auto-reply/reply/queue.js";
6
7
  import { loadConfig } from "../../config/config.js";
@@ -10,7 +11,8 @@ import { archiveFileOnDisk, listSessionsFromStore, loadCombinedSessionStoreForGa
10
11
  import { applySessionsPatchToStore } from "../sessions-patch.js";
11
12
  import { resolveSessionKeyFromResolveParams } from "../sessions-resolve.js";
12
13
  export const sessionsHandlers = {
13
- "sessions.list": ({ params, respond }) => {
14
+ "sessions.list": async ({ params, respond }) => {
15
+ await modelCacheReady;
14
16
  if (!validateSessionsListParams(params)) {
15
17
  respond(false, undefined, errorShape(ErrorCodes.INVALID_REQUEST, `invalid sessions.list params: ${formatValidationErrors(validateSessionsListParams.errors)}`));
16
18
  return;
@@ -4,11 +4,11 @@ import { resolveAgentWorkspaceDir, resolveAgentWorkspaceRoot, resolveDefaultAgen
4
4
  import { installSkill } from "../../agents/skills-install.js";
5
5
  import { buildWorkspaceSkillStatus } from "../../agents/skills-status.js";
6
6
  import { loadWorkspaceSkillEntries, resolveBundledSkillsDir, } from "../../agents/skills.js";
7
- import { extractEmbedFlag, applyEmbedFlag } from "../../agents/skills/frontmatter.js";
7
+ import { extractEmbedFlag, applyEmbedFlag, applySkillMetadataFields, } from "../../agents/skills/frontmatter.js";
8
8
  import { bumpSkillsSnapshotVersion } from "../../agents/skills/refresh.js";
9
9
  import { loadConfig, writeConfigFile } from "../../config/config.js";
10
10
  import { getRemoteSkillEligibility } from "../../infra/skills-remote.js";
11
- import { ErrorCodes, errorShape, formatValidationErrors, validateSkillsBinsParams, validateSkillsCreateParams, validateSkillsDeleteDraftParams, validateSkillsDeleteParams, validateSkillsDraftsParams, validateSkillsInstallParams, validateSkillsReadParams, validateSkillsSaveDraftParams, validateSkillsStatusParams, validateSkillsUpdateParams, } from "../protocol/index.js";
11
+ import { ErrorCodes, errorShape, formatValidationErrors, validateSkillsBinsParams, validateSkillsCreateParams, validateSkillsDeleteDraftParams, validateSkillsDeleteParams, validateSkillsDraftsParams, validateSkillsInstallParams, validateSkillsReadParams, validateSkillsSaveDraftParams, validateSkillsPatchParams, validateSkillsStatusParams, validateSkillsUpdateParams, } from "../protocol/index.js";
12
12
  function listWorkspaceDirs(cfg) {
13
13
  const dirs = new Set();
14
14
  const list = cfg.agents?.list;
@@ -163,6 +163,35 @@ export const skillsHandlers = {
163
163
  await writeConfigFile(nextConfig);
164
164
  respond(true, { ok: true, skillKey: p.skillKey, config: current }, undefined);
165
165
  },
166
+ "skills.patch": ({ params, respond }) => {
167
+ if (!validateSkillsPatchParams(params)) {
168
+ respond(false, undefined, errorShape(ErrorCodes.INVALID_REQUEST, `invalid skills.patch params: ${formatValidationErrors(validateSkillsPatchParams.errors)}`));
169
+ return;
170
+ }
171
+ const { name, icon, tools } = params;
172
+ const cfg = loadConfig();
173
+ const workspaceDir = resolveWorkspaceRoot(cfg);
174
+ const skillDir = path.join(workspaceDir, "skills", name);
175
+ const skillMdPath = path.join(skillDir, "SKILL.md");
176
+ if (!fs.existsSync(skillMdPath)) {
177
+ respond(false, undefined, errorShape(ErrorCodes.INVALID_REQUEST, `Skill not found: ${name}`));
178
+ return;
179
+ }
180
+ try {
181
+ const content = fs.readFileSync(skillMdPath, "utf8");
182
+ const fields = {};
183
+ if ("icon" in params)
184
+ fields.icon = icon;
185
+ if ("tools" in params)
186
+ fields.tools = tools;
187
+ const updated = applySkillMetadataFields(content, fields);
188
+ fs.writeFileSync(skillMdPath, updated, "utf8");
189
+ respond(true, { ok: true });
190
+ }
191
+ catch (err) {
192
+ respond(false, undefined, errorShape(ErrorCodes.UNAVAILABLE, String(err)));
193
+ }
194
+ },
166
195
  "skills.read": ({ params, respond }) => {
167
196
  if (!validateSkillsReadParams(params)) {
168
197
  respond(false, undefined, errorShape(ErrorCodes.INVALID_REQUEST, `invalid skills.read params: ${formatValidationErrors(validateSkillsReadParams.errors)}`));
@@ -0,0 +1,7 @@
1
+ import { buildSelectableToolList } from "../../agents/tool-policy.js";
2
+ export const toolsHandlers = {
3
+ "tools.list": ({ respond }) => {
4
+ const tools = buildSelectableToolList();
5
+ respond(true, { tools });
6
+ },
7
+ };
@@ -44,6 +44,7 @@ import { brandHandlers } from "./server-methods/brand.js";
44
44
  import { businessHandlers } from "./server-methods/business.js";
45
45
  import { whatsappConversationsHandlers } from "./server-methods/whatsapp-conversations.js";
46
46
  import { relayHandlers } from "./server-methods/relay.js";
47
+ import { toolsHandlers } from "./server-methods/tools.js";
47
48
  const ADMIN_SCOPE = "operator.admin";
48
49
  const READ_SCOPE = "operator.read";
49
50
  const WRITE_SCOPE = "operator.write";
@@ -116,6 +117,7 @@ const READ_METHODS = new Set([
116
117
  "whatsapp.conversations",
117
118
  "whatsapp.messages",
118
119
  "whatsapp.groupInfo",
120
+ "tools.list",
119
121
  ]);
120
122
  const WRITE_METHODS = new Set([
121
123
  "send",
@@ -266,6 +268,7 @@ export const coreGatewayHandlers = {
266
268
  ...wifiHandlers,
267
269
  ...whatsappConversationsHandlers,
268
270
  ...relayHandlers,
271
+ ...toolsHandlers,
269
272
  };
270
273
  export async function handleGatewayRequest(opts) {
271
274
  const { req, respond, client, isWebchatConnect, context } = opts;
@@ -112,7 +112,11 @@ export function loadSessionEntry(sessionKey) {
112
112
  const agentId = resolveSessionStoreAgentId(cfg, canonicalKey);
113
113
  const storePath = resolveStorePath(sessionCfg?.store, { agentId });
114
114
  const store = loadSessionStore(storePath);
115
- const entry = store[canonicalKey];
115
+ // Try canonical key first, then fall back to raw key.
116
+ // The runtime's resolveSessionKey stores entries under the raw key (e.g.
117
+ // "webchat:admin:ts") but resolveSessionStoreKey canonicalizes to
118
+ // "agent:admin:webchat:admin:ts". Both forms must be checked.
119
+ const entry = store[canonicalKey] ?? store[sessionKey];
116
120
  return { cfg, storePath, store, entry, canonicalKey };
117
121
  }
118
122
  export function classifySessionKey(key, entry) {
@@ -43,12 +43,26 @@ export async function applyMediaUnderstanding(params) {
43
43
  if (decisions.length > 0) {
44
44
  ctx.MediaUnderstandingDecisions = [...(ctx.MediaUnderstandingDecisions ?? []), ...decisions];
45
45
  }
46
- // Surface audio failures so the agent can inform the user instead of receiving
47
- // a bare <media:audio> placeholder with no context about what went wrong.
46
+ // Surface media download failures so the agent can inform the user instead of
47
+ // receiving a bare <media:TYPE> placeholder with no context about what went wrong.
48
+ const bodyHint = ctx.CommandBody ?? ctx.RawBody ?? ctx.Body ?? "";
49
+ const mediaPlaceholderMatch = /^<media:(\w+)>/i.exec(bodyHint.trim());
50
+ const isAudioPlaceholder = mediaPlaceholderMatch?.[1]?.toLowerCase() === "audio";
51
+ if (ctx.MediaDownloadFailed && mediaPlaceholderMatch && !isAudioPlaceholder) {
52
+ const mediaType = mediaPlaceholderMatch[1]?.toLowerCase() ?? "file";
53
+ const reasonSuffix = ctx.MediaDownloadFailedReason
54
+ ? ` (${ctx.MediaDownloadFailedReason})`
55
+ : "";
56
+ const note = `[Media download failed — the ${mediaType} could not be retrieved from WhatsApp${reasonSuffix}. Ask the user to resend or share the content directly.]`;
57
+ logVerbose(`applyMediaUnderstanding: ${note}`);
58
+ ctx.Body = note;
59
+ ctx.CommandBody = note;
60
+ ctx.RawBody = note;
61
+ finalizeInboundContext(ctx, { forceBodyForAgent: true, forceBodyForCommands: true });
62
+ }
63
+ // Surface audio failures with more detailed provider-specific reasons.
48
64
  const audioDecision = decisions.find((d) => d.capability === "audio");
49
65
  const audioTranscribed = outputs.some((o) => o.kind === "audio.transcription");
50
- const bodyHint = ctx.CommandBody ?? ctx.RawBody ?? ctx.Body ?? "";
51
- const isAudioPlaceholder = /^<media:audio>/i.test(bodyHint.trim());
52
66
  if (isAudioPlaceholder && !audioTranscribed) {
53
67
  let reason;
54
68
  if (ctx.MediaDownloadFailed) {
@@ -267,6 +267,7 @@ export async function processMessage(params) {
267
267
  MediaUrl: params.msg.mediaUrl,
268
268
  MediaType: params.msg.mediaType,
269
269
  MediaDownloadFailed: params.msg.mediaDownloadFailed,
270
+ MediaDownloadFailedReason: params.msg.mediaDownloadFailedReason,
270
271
  ChatType: params.msg.chatType,
271
272
  ConversationLabel: params.msg.chatType === "group" ? conversationId : params.msg.from,
272
273
  GroupSubject: params.msg.groupSubject,
@@ -297,6 +297,7 @@ export async function monitorWebInbox(options) {
297
297
  let mediaPath;
298
298
  let mediaType;
299
299
  let mediaDownloadFailed = false;
300
+ let mediaDownloadFailedReason;
300
301
  try {
301
302
  const inboundMedia = await downloadInboundMedia(msg, sock);
302
303
  if (inboundMedia) {
@@ -311,11 +312,13 @@ export async function monitorWebInbox(options) {
311
312
  else if (body.includes("<media:")) {
312
313
  // downloadInboundMedia returned undefined — media was expected but unavailable
313
314
  mediaDownloadFailed = true;
315
+ mediaDownloadFailedReason = "media could not be downloaded";
314
316
  inboundLogger.warn({ id, from, body }, "media download returned empty");
315
317
  }
316
318
  }
317
319
  catch (err) {
318
320
  mediaDownloadFailed = body.includes("<media:");
321
+ mediaDownloadFailedReason = String(err).replace(/^Error:\s*/, "");
319
322
  inboundLogger.warn({ id, from, error: String(err) }, "media download failed");
320
323
  }
321
324
  const chatJid = remoteJid;
@@ -378,6 +381,7 @@ export async function monitorWebInbox(options) {
378
381
  mediaPath,
379
382
  mediaType,
380
383
  mediaDownloadFailed,
384
+ mediaDownloadFailedReason,
381
385
  businessClosed,
382
386
  };
383
387
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rubytech/taskmaster",
3
- "version": "1.42.0",
3
+ "version": "1.42.1",
4
4
  "description": "AI-powered business assistant for small businesses",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -60,16 +60,30 @@ For each reference file:
60
60
 
61
61
  If the skill is simple enough to fit in SKILL.md alone (under ~30 lines of instructions), skip references.
62
62
 
63
- ## Step 5: Compose the Skill
63
+ ## Step 5: Choose an Icon
64
+
65
+ Ask: **"Would you like to pick an icon for this skill?"**
66
+
67
+ Icons give skills a visual identity in the Control Panel. They use [Lucide](https://lucide.dev) icon names. Offer a few suggestions that match the skill's purpose — the reference file `references/lean-pattern.md` has the full curated list.
68
+
69
+ If the user picks one, include `"icon": "<name>"` in the `metadata.taskmaster` block in frontmatter. If they skip this, omit the field — it's optional.
70
+
71
+ ## Step 5b: Identify Required Tools
72
+
73
+ If the skill needs specific agent tools (web search, memory, file access, etc.), note them. This matters when the skill will be assigned to a sub-agent — sub-agents have restricted tool access, so the skill must declare which tools it needs via the `tools` field in `metadata.taskmaster`.
74
+
75
+ For skills on the primary agent, tools are already available and this field is unnecessary. Only add `tools` when the user indicates the skill targets a sub-agent.
76
+
77
+ ## Step 6: Compose the Skill
64
78
 
65
79
  Using the lean pattern from `references/lean-pattern.md`, compose:
66
80
 
67
- 1. **SKILL.md** — frontmatter (`name`, `description`) + activation conditions + behaviour rules + references index
81
+ 1. **SKILL.md** — frontmatter (`name`, `description`, optional `metadata.taskmaster` with `icon`/`tools`) + activation conditions + behaviour rules + references index
68
82
  2. **Reference files** — one per detailed topic
69
83
 
70
84
  Show the user the complete SKILL.md content and each reference file. Ask them to review.
71
85
 
72
- ## Step 6: Save the Draft
86
+ ## Step 7: Save the Draft
73
87
 
74
88
  Use the `skill_draft_save` tool:
75
89
 
@@ -85,7 +99,7 @@ skill_draft_save({
85
99
 
86
100
  The tool saves the draft to the correct location where the Control Panel looks for drafts. Do not use `memory_write` or `write` — those save to the wrong location.
87
101
 
88
- ## Step 7: Direct to Control Panel
102
+ ## Step 8: Direct to Control Panel
89
103
 
90
104
  Tell the user:
91
105
 
@@ -55,18 +55,28 @@ Load `references/templates.md` for [what it covers].
55
55
  ## Optional Frontmatter Fields
56
56
 
57
57
  ```yaml
58
- metadata: {"taskmaster":{"always":true,"emoji":"📦","primaryEnv":"MY_API_KEY"}}
58
+ metadata: {"taskmaster":{"always":true,"icon":"Calendar","emoji":"📦","primaryEnv":"MY_API_KEY"}}
59
59
  ```
60
60
 
61
61
  | Field | Purpose |
62
62
  |-------|---------|
63
63
  | `always` | Always include in prompt (skip eligibility checks) |
64
- | `emoji` | Display emoji in the Control Panel |
64
+ | `icon` | Lucide icon name displayed in the Control Panel skill card. Gives the skill a recognisable visual identity. See icon list below. |
65
+ | `emoji` | Fallback display emoji — used only when no `icon` is set |
65
66
  | `primaryEnv` | Env var for API key — enables the key input field in the UI |
67
+ | `tools` | Array of tool names the skill needs (e.g. `["web_search", "memory_write"]`). When a skill is assigned to a sub-agent, the tools listed here are made available to that agent. Only relevant for skills that will run on sub-agents with restricted tool access. |
66
68
  | `requires.bins` | Required binaries (e.g. `["curl"]`) |
67
69
  | `requires.env` | Required environment variables |
68
70
 
69
- Most user-created skills need only `name` and `description`.
71
+ Most user-created skills need only `name` and `description`. Add `icon` for a polished look in the Control Panel.
72
+
73
+ ### Available Icon Names
74
+
75
+ These are [Lucide](https://lucide.dev) icon names. Choose one that reflects the skill's purpose:
76
+
77
+ `Zap` · `Globe` · `BookOpen` · `Calendar` · `Camera` · `Clock` · `Cloud` · `Code` · `Database` · `FileText` · `Headphones` · `Heart` · `Home` · `Image` · `Mail` · `Map` · `MessageSquare` · `Music` · `Phone` · `Puzzle` · `Search` · `Settings` · `ShoppingCart` · `Star` · `Tag` · `Truck` · `User` · `Wallet` · `Wrench` · `Cpu`
78
+
79
+ Any valid Lucide icon name works — this list is a starting point, not a constraint.
70
80
 
71
81
  ## Examples
72
82