mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-05 15:00:21 +00:00
fix(plugins): late-binding subagent runtime for non-gateway load paths (#46648)
Merged via squash.
Prepared head SHA: 44742652c9
Co-authored-by: jalehman <550978+jalehman@users.noreply.github.com>
Co-authored-by: jalehman <550978+jalehman@users.noreply.github.com>
Reviewed-by: @jalehman
This commit is contained in:
@@ -323,6 +323,7 @@ export async function runAgentTurnWithFallback(params: {
|
||||
try {
|
||||
const result = await runEmbeddedPiAgent({
|
||||
...embeddedContext,
|
||||
allowGatewaySubagentBinding: true,
|
||||
trigger: params.isHeartbeat ? "heartbeat" : "user",
|
||||
groupId: resolveGroupSessionKey(params.sessionCtx)?.id,
|
||||
groupChannel:
|
||||
|
||||
@@ -494,6 +494,7 @@ export async function runMemoryFlushIfNeeded(params: {
|
||||
...embeddedContext,
|
||||
...senderContext,
|
||||
...runBaseParams,
|
||||
allowGatewaySubagentBinding: true,
|
||||
trigger: "memory",
|
||||
memoryFlushWritePath,
|
||||
prompt: resolveMemoryFlushPromptForRun({
|
||||
|
||||
@@ -78,6 +78,7 @@ export const handleCompactCommand: CommandHandler = async (params) => {
|
||||
const result = await compactEmbeddedPiSession({
|
||||
sessionId,
|
||||
sessionKey: params.sessionKey,
|
||||
allowGatewaySubagentBinding: true,
|
||||
messageChannel: params.command.channel,
|
||||
groupId: params.sessionEntry.groupId,
|
||||
groupChannel: params.sessionEntry.groupChannel,
|
||||
|
||||
127
src/auto-reply/reply/commands-system-prompt.test.ts
Normal file
127
src/auto-reply/reply/commands-system-prompt.test.ts
Normal file
@@ -0,0 +1,127 @@
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import type { HandleCommandsParams } from "./commands-types.js";
|
||||
|
||||
const { createOpenClawCodingToolsMock } = vi.hoisted(() => ({
|
||||
createOpenClawCodingToolsMock: vi.fn(() => []),
|
||||
}));
|
||||
|
||||
vi.mock("../../agents/bootstrap-files.js", () => ({
|
||||
resolveBootstrapContextForRun: vi.fn(async () => ({
|
||||
bootstrapFiles: [],
|
||||
contextFiles: [],
|
||||
})),
|
||||
}));
|
||||
|
||||
vi.mock("../../agents/pi-tools.js", () => ({
|
||||
createOpenClawCodingTools: createOpenClawCodingToolsMock,
|
||||
}));
|
||||
|
||||
vi.mock("../../agents/sandbox.js", () => ({
|
||||
resolveSandboxRuntimeStatus: vi.fn(() => ({ sandboxed: false, mode: "off" })),
|
||||
}));
|
||||
|
||||
vi.mock("../../agents/skills.js", () => ({
|
||||
buildWorkspaceSkillSnapshot: vi.fn(() => ({ prompt: "", skills: [], resolvedSkills: [] })),
|
||||
}));
|
||||
|
||||
vi.mock("../../agents/skills/refresh.js", () => ({
|
||||
getSkillsSnapshotVersion: vi.fn(() => "test-snapshot"),
|
||||
}));
|
||||
|
||||
vi.mock("../../agents/agent-scope.js", () => ({
|
||||
resolveSessionAgentIds: vi.fn(() => ({ sessionAgentId: "main" })),
|
||||
}));
|
||||
|
||||
vi.mock("../../agents/model-selection.js", () => ({
|
||||
resolveDefaultModelForAgent: vi.fn(() => ({ provider: "openai", model: "gpt-5" })),
|
||||
}));
|
||||
|
||||
vi.mock("../../agents/system-prompt-params.js", () => ({
|
||||
buildSystemPromptParams: vi.fn(() => ({
|
||||
runtimeInfo: { host: "unknown", os: "unknown", arch: "unknown", node: process.version },
|
||||
userTimezone: "UTC",
|
||||
userTime: "12:00 PM",
|
||||
userTimeFormat: "12h",
|
||||
})),
|
||||
}));
|
||||
|
||||
vi.mock("../../agents/system-prompt.js", () => ({
|
||||
buildAgentSystemPrompt: vi.fn(() => "system prompt"),
|
||||
}));
|
||||
|
||||
vi.mock("../../agents/tool-summaries.js", () => ({
|
||||
buildToolSummaryMap: vi.fn(() => ({})),
|
||||
}));
|
||||
|
||||
vi.mock("../../infra/skills-remote.js", () => ({
|
||||
getRemoteSkillEligibility: vi.fn(() => false),
|
||||
}));
|
||||
|
||||
vi.mock("../../tts/tts.js", () => ({
|
||||
buildTtsSystemPromptHint: vi.fn(() => undefined),
|
||||
}));
|
||||
|
||||
import { resolveCommandsSystemPromptBundle } from "./commands-system-prompt.js";
|
||||
|
||||
function makeParams(): HandleCommandsParams {
|
||||
return {
|
||||
ctx: {
|
||||
SessionKey: "agent:main:default",
|
||||
},
|
||||
cfg: {},
|
||||
command: {
|
||||
surface: "telegram",
|
||||
channel: "telegram",
|
||||
ownerList: [],
|
||||
senderIsOwner: true,
|
||||
isAuthorizedSender: true,
|
||||
rawBodyNormalized: "/context",
|
||||
commandBodyNormalized: "/context",
|
||||
},
|
||||
directives: {},
|
||||
elevated: {
|
||||
enabled: true,
|
||||
allowed: true,
|
||||
failures: [],
|
||||
},
|
||||
agentId: "main",
|
||||
sessionEntry: {
|
||||
sessionId: "session-1",
|
||||
groupId: "group-1",
|
||||
groupChannel: "#general",
|
||||
space: "guild-1",
|
||||
spawnedBy: "agent:parent",
|
||||
},
|
||||
sessionKey: "agent:main:default",
|
||||
workspaceDir: "/tmp/workspace",
|
||||
defaultGroupActivation: () => "mention",
|
||||
resolvedVerboseLevel: "off",
|
||||
resolvedReasoningLevel: "off",
|
||||
resolvedElevatedLevel: "off",
|
||||
resolveDefaultThinkingLevel: async () => undefined,
|
||||
provider: "openai",
|
||||
model: "gpt-5.4",
|
||||
contextTokens: 0,
|
||||
isGroup: false,
|
||||
} as unknown as HandleCommandsParams;
|
||||
}
|
||||
|
||||
describe("resolveCommandsSystemPromptBundle", () => {
|
||||
beforeEach(() => {
|
||||
createOpenClawCodingToolsMock.mockClear();
|
||||
createOpenClawCodingToolsMock.mockReturnValue([]);
|
||||
});
|
||||
|
||||
it("opts command tool builds into gateway subagent binding", async () => {
|
||||
await resolveCommandsSystemPromptBundle(makeParams());
|
||||
|
||||
expect(createOpenClawCodingToolsMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
allowGatewaySubagentBinding: true,
|
||||
sessionKey: "agent:main:default",
|
||||
workspaceDir: "/tmp/workspace",
|
||||
messageProvider: "telegram",
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -57,6 +57,7 @@ export async function resolveCommandsSystemPromptBundle(
|
||||
agentId: params.agentId,
|
||||
workspaceDir,
|
||||
sessionKey: params.sessionKey,
|
||||
allowGatewaySubagentBinding: true,
|
||||
messageProvider: params.command.channel,
|
||||
groupId: params.sessionEntry?.groupId ?? undefined,
|
||||
groupChannel: params.sessionEntry?.groupChannel ?? undefined,
|
||||
|
||||
@@ -599,6 +599,7 @@ describe("/compact command", () => {
|
||||
expect.objectContaining({
|
||||
sessionId: "session-1",
|
||||
sessionKey: "agent:main:main",
|
||||
allowGatewaySubagentBinding: true,
|
||||
trigger: "manual",
|
||||
customInstructions: "focus on decisions",
|
||||
messageChannel: "whatsapp",
|
||||
|
||||
@@ -287,10 +287,12 @@ describe("createFollowupRunner bootstrap warning dedupe", () => {
|
||||
|
||||
const call = runEmbeddedPiAgentMock.mock.calls.at(-1)?.[0] as
|
||||
| {
|
||||
allowGatewaySubagentBinding?: boolean;
|
||||
bootstrapPromptWarningSignaturesSeen?: string[];
|
||||
bootstrapPromptWarningSignature?: string;
|
||||
}
|
||||
| undefined;
|
||||
expect(call?.allowGatewaySubagentBinding).toBe(true);
|
||||
expect(call?.bootstrapPromptWarningSignaturesSeen).toEqual(["sig-a", "sig-b"]);
|
||||
expect(call?.bootstrapPromptWarningSignature).toBe("sig-b");
|
||||
});
|
||||
|
||||
@@ -171,6 +171,7 @@ export function createFollowupRunner(params: {
|
||||
let attemptCompactionCount = 0;
|
||||
try {
|
||||
const result = await runEmbeddedPiAgent({
|
||||
allowGatewaySubagentBinding: true,
|
||||
sessionId: queued.run.sessionId,
|
||||
sessionKey: queued.run.sessionKey,
|
||||
agentId: queued.run.agentId,
|
||||
|
||||
@@ -220,6 +220,7 @@ export async function handleInlineActions(params: {
|
||||
agentDir,
|
||||
workspaceDir,
|
||||
config: cfg,
|
||||
allowGatewaySubagentBinding: true,
|
||||
});
|
||||
const authorizedTools = applyOwnerOnlyToolPolicy(tools, command.senderIsOwner);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user