mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-06 06:42:52 +00:00
fix(commands): preserve async skill commands
This commit is contained in:
@@ -213,6 +213,25 @@ describe("info command handlers", () => {
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
|
||||
it("loads skills asynchronously before deciding named /skill invocations", async () => {
|
||||
const params = buildInfoParams("/skill demo_skill input", {
|
||||
commands: { text: true },
|
||||
} as OpenClawConfig);
|
||||
params.loadSkillCommands = vi.fn(async () => [
|
||||
{
|
||||
name: "demo_skill",
|
||||
skillName: "demo-skill",
|
||||
description: "Demo skill",
|
||||
},
|
||||
]);
|
||||
|
||||
const result = await handleSkillCommandUsage(params, true);
|
||||
|
||||
expect(result).toBeNull();
|
||||
expect(params.loadSkillCommands).toHaveBeenCalledOnce();
|
||||
expect(listSkillCommandsForAgentsMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("uses the canonical command sender identity for /whoami AllowFrom", async () => {
|
||||
const params = buildInfoParams(
|
||||
"/whoami",
|
||||
|
||||
@@ -21,6 +21,22 @@ import { resolveReplyToMode } from "./reply-threading.js";
|
||||
export { handleContextCommand } from "./commands-context-command.js";
|
||||
export { handleWhoamiCommand } from "./commands-whoami.js";
|
||||
|
||||
async function resolveSkillCommands(params: HandleCommandsParams) {
|
||||
if (params.skillCommands !== undefined) {
|
||||
return params.skillCommands;
|
||||
}
|
||||
if (params.loadSkillCommands) {
|
||||
return params.loadSkillCommands();
|
||||
}
|
||||
const agentId = params.sessionKey
|
||||
? resolveSessionAgentId({ sessionKey: params.sessionKey, config: params.cfg })
|
||||
: params.agentId;
|
||||
return listSkillCommandsForAgents({
|
||||
cfg: params.cfg,
|
||||
agentIds: agentId ? [agentId] : undefined,
|
||||
});
|
||||
}
|
||||
|
||||
export const handleHelpCommand: CommandHandler = async (params, allowTextCommands) => {
|
||||
if (!allowTextCommands) {
|
||||
return null;
|
||||
@@ -56,12 +72,7 @@ export const handleCommandsListCommand: CommandHandler = async (params, allowTex
|
||||
const agentId = params.sessionKey
|
||||
? resolveSessionAgentId({ sessionKey: params.sessionKey, config: params.cfg })
|
||||
: params.agentId;
|
||||
const skillCommands =
|
||||
params.skillCommands ??
|
||||
listSkillCommandsForAgents({
|
||||
cfg: params.cfg,
|
||||
agentIds: agentId ? [agentId] : undefined,
|
||||
});
|
||||
const skillCommands = await resolveSkillCommands(params);
|
||||
const surface = params.ctx.Surface;
|
||||
const commandPlugin = surface ? getChannelPlugin(surface) : null;
|
||||
const paginated = buildCommandsMessagePaginated(params.cfg, skillCommands, {
|
||||
@@ -123,15 +134,7 @@ export const handleSkillCommandUsage: CommandHandler = async (params, allowTextC
|
||||
}
|
||||
|
||||
const [, rawName] = normalized.match(/^\/skill(?:\s+([^\s]+))?/u) ?? [];
|
||||
const agentId = params.sessionKey
|
||||
? resolveSessionAgentId({ sessionKey: params.sessionKey, config: params.cfg })
|
||||
: params.agentId;
|
||||
const skillCommands =
|
||||
params.skillCommands ??
|
||||
listSkillCommandsForAgents({
|
||||
cfg: params.cfg,
|
||||
agentIds: agentId ? [agentId] : undefined,
|
||||
});
|
||||
const skillCommands = await resolveSkillCommands(params);
|
||||
if (
|
||||
rawName &&
|
||||
resolveSkillCommandInvocation({ commandBodyNormalized: normalized, skillCommands })
|
||||
|
||||
@@ -65,6 +65,7 @@ export type HandleCommandsParams = {
|
||||
contextTokens: number;
|
||||
isGroup: boolean;
|
||||
skillCommands?: SkillCommandSpec[];
|
||||
loadSkillCommands?: () => Promise<SkillCommandSpec[]>;
|
||||
typing?: TypingController;
|
||||
};
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
resolveThinkingDefaultWithRuntimeCatalog,
|
||||
type ModelAliasIndex,
|
||||
} from "../../agents/model-selection.js";
|
||||
import type { SkillCommandSpec } from "../../agents/skills.js";
|
||||
import type { OpenClawConfig } from "../../config/config.js";
|
||||
import { createLazyImportLoader } from "../../shared/lazy-promise.js";
|
||||
import { normalizeOptionalString } from "../../shared/string-coerce.js";
|
||||
@@ -20,14 +21,22 @@ import { stripStructuralPrefixes } from "./mentions.js";
|
||||
import type { createTypingController } from "./typing.js";
|
||||
|
||||
type AgentDefaults = NonNullable<NonNullable<OpenClawConfig["agents"]>["defaults"]> | undefined;
|
||||
type SkillCommandsRuntime = typeof import("../skill-commands.runtime.js");
|
||||
|
||||
const commandsRuntimeLoader = createLazyImportLoader(() => import("./commands.runtime.js"));
|
||||
const skillCommandsRuntimeLoader = createLazyImportLoader<SkillCommandsRuntime>(
|
||||
() => import("../skill-commands.runtime.js"),
|
||||
);
|
||||
const statusCommandRuntimeLoader = createLazyImportLoader(() => import("./commands-status.js"));
|
||||
|
||||
function loadCommandsRuntime() {
|
||||
return commandsRuntimeLoader.load();
|
||||
}
|
||||
|
||||
function loadSkillCommandsRuntime() {
|
||||
return skillCommandsRuntimeLoader.load();
|
||||
}
|
||||
|
||||
function loadStatusCommandRuntime() {
|
||||
return statusCommandRuntimeLoader.load();
|
||||
}
|
||||
@@ -139,6 +148,17 @@ export async function maybeResolveNativeSlashCommandFastReply(params: {
|
||||
};
|
||||
}
|
||||
|
||||
let loadedSkillCommands: SkillCommandSpec[] | undefined;
|
||||
const loadNativeSkillCommands = async () => {
|
||||
loadedSkillCommands ??= (await loadSkillCommandsRuntime()).listSkillCommandsForWorkspace({
|
||||
workspaceDir: params.workspaceDir,
|
||||
cfg: params.cfg,
|
||||
agentId: params.agentId,
|
||||
skillFilter: params.skillFilter,
|
||||
});
|
||||
return loadedSkillCommands;
|
||||
};
|
||||
|
||||
const commandResult = await (
|
||||
await loadCommandsRuntime()
|
||||
).handleCommands({
|
||||
@@ -174,7 +194,7 @@ export async function maybeResolveNativeSlashCommandFastReply(params: {
|
||||
model: params.model,
|
||||
contextTokens: params.agentCfg?.contextTokens ?? 0,
|
||||
isGroup: sessionState.isGroup,
|
||||
skillCommands: [],
|
||||
loadSkillCommands: loadNativeSkillCommands,
|
||||
typing: params.typing,
|
||||
});
|
||||
if (!commandResult.shouldContinue) {
|
||||
@@ -232,7 +252,7 @@ export async function maybeResolveNativeSlashCommandFastReply(params: {
|
||||
allowTextCommands: directiveResult.result.allowTextCommands,
|
||||
inlineStatusRequested: directiveResult.result.inlineStatusRequested,
|
||||
command: directiveResult.result.command,
|
||||
skillCommands: directiveResult.result.skillCommands,
|
||||
skillCommands: loadedSkillCommands ?? directiveResult.result.skillCommands,
|
||||
directives: directiveResult.result.directives,
|
||||
cleanedBody: directiveResult.result.cleanedBody,
|
||||
elevatedEnabled: directiveResult.result.elevatedEnabled,
|
||||
|
||||
Reference in New Issue
Block a user