Skills: expand direct slash dispatch regression coverage

This commit is contained in:
Vincent Koc 2026-03-14 23:19:20 -07:00
parent 6b47a95c6f
commit b71d5609d9
2 changed files with 46 additions and 12 deletions

View File

@ -43,7 +43,7 @@ const createTypingController = (): TypingController => ({
cleanup: vi.fn(), cleanup: vi.fn(),
}); });
const skillCommands: SkillCommandSpec[] = [ const defaultSkillCommands: SkillCommandSpec[] = [
{ {
name: "danger-skill", name: "danger-skill",
skillName: "danger-skill", skillName: "danger-skill",
@ -53,12 +53,26 @@ const skillCommands: SkillCommandSpec[] = [
toolName: "gateway", toolName: "gateway",
}, },
}, },
{
name: "read-skill",
skillName: "read-skill",
description: "Allowed direct tool dispatch",
dispatch: {
kind: "tool",
toolName: "read",
},
},
]; ];
function createInput(): HandleInlineActionsInput { function createInput(overrides?: {
body?: string;
senderIsOwner?: boolean;
skillCommands?: SkillCommandSpec[];
}): HandleInlineActionsInput {
const body = overrides?.body ?? "/danger-skill test";
const ctx = buildTestCtx({ const ctx = buildTestCtx({
Body: "/danger-skill test", Body: body,
CommandBody: "/danger-skill test", CommandBody: body,
Provider: "whatsapp", Provider: "whatsapp",
Surface: "whatsapp", Surface: "whatsapp",
From: "whatsapp:+123", From: "whatsapp:+123",
@ -85,17 +99,17 @@ function createInput(): HandleInlineActionsInput {
channel: "whatsapp", channel: "whatsapp",
channelId: "whatsapp", channelId: "whatsapp",
ownerList: [], ownerList: [],
senderIsOwner: true, senderIsOwner: overrides?.senderIsOwner ?? true,
isAuthorizedSender: true, isAuthorizedSender: true,
senderId: "owner-1", senderId: "owner-1",
abortKey: "whatsapp:+123", abortKey: "whatsapp:+123",
rawBodyNormalized: "/danger-skill test", rawBodyNormalized: body,
commandBodyNormalized: "/danger-skill test", commandBodyNormalized: body,
from: "whatsapp:+123", from: "whatsapp:+123",
to: "whatsapp:+123", to: "whatsapp:+123",
}, },
directives: clearInlineDirectives("/danger-skill test"), directives: clearInlineDirectives(body),
cleanedBody: "/danger-skill test", cleanedBody: body,
elevatedEnabled: false, elevatedEnabled: false,
elevatedAllowed: false, elevatedAllowed: false,
elevatedFailures: [], elevatedFailures: [],
@ -110,7 +124,7 @@ function createInput(): HandleInlineActionsInput {
contextTokens: 0, contextTokens: 0,
abortedLastRun: false, abortedLastRun: false,
sessionScope: "per-sender", sessionScope: "per-sender",
skillCommands, skillCommands: overrides?.skillCommands ?? defaultSkillCommands,
}; };
} }
@ -130,4 +144,24 @@ describe("handleInlineActions skill tool dispatch", () => {
}); });
expect(gatewayExecuteMock).not.toHaveBeenCalled(); expect(gatewayExecuteMock).not.toHaveBeenCalled();
}); });
it("executes an allowed tool through direct /skill dispatch", async () => {
const result = await handleInlineActions(createInput({ body: "/read-skill test" }));
expect(result).toEqual({
kind: "reply",
reply: { text: "READ" },
});
expect(readExecuteMock).toHaveBeenCalled();
});
it("keeps owner-only tools blocked for non-owners before policy resolution", async () => {
const result = await handleInlineActions(createInput({ senderIsOwner: false }));
expect(result).toEqual({
kind: "reply",
reply: { text: "❌ Tool not available: gateway" },
});
expect(gatewayExecuteMock).not.toHaveBeenCalled();
});
}); });

View File

@ -127,7 +127,7 @@ function resolveSkillDispatchTools(params: {
}); });
const toolsByAuthorization = applyOwnerOnlyToolPolicy(tools, params.senderIsOwner); const toolsByAuthorization = applyOwnerOnlyToolPolicy(tools, params.senderIsOwner);
const { const {
agentId, agentId: resolvedAgentId,
globalPolicy, globalPolicy,
globalProviderPolicy, globalProviderPolicy,
agentPolicy, agentPolicy,
@ -194,7 +194,7 @@ function resolveSkillDispatchTools(params: {
agentPolicy, agentPolicy,
agentProviderPolicy, agentProviderPolicy,
groupPolicy, groupPolicy,
agentId, agentId: resolvedAgentId,
}), }),
{ policy: sandboxPolicy, label: "sandbox tools.allow" }, { policy: sandboxPolicy, label: "sandbox tools.allow" },
{ policy: subagentPolicy, label: "subagent tools.allow" }, { policy: subagentPolicy, label: "subagent tools.allow" },