mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-23 07:01:40 +00:00
test: narrow subagent spawn test seams
This commit is contained in:
23
src/agents/subagent-spawn.runtime.ts
Normal file
23
src/agents/subagent-spawn.runtime.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
export { formatThinkingLevels, normalizeThinkLevel } from "../auto-reply/thinking.js";
|
||||
export { DEFAULT_SUBAGENT_MAX_SPAWN_DEPTH } from "../config/agent-limits.js";
|
||||
export { loadConfig } from "../config/config.js";
|
||||
export { mergeSessionEntry, updateSessionStore } from "../config/sessions.js";
|
||||
export { callGateway } from "../gateway/call.js";
|
||||
export { ADMIN_SCOPE, isAdminOnlyMethod } from "../gateway/method-scopes.js";
|
||||
export {
|
||||
pruneLegacyStoreKeys,
|
||||
resolveGatewaySessionStoreTarget,
|
||||
} from "../gateway/session-utils.js";
|
||||
export { getGlobalHookRunner } from "../plugins/hook-runner-global.js";
|
||||
export { emitSessionLifecycleEvent } from "../sessions/session-lifecycle-events.js";
|
||||
export { normalizeDeliveryContext } from "../utils/delivery-context.js";
|
||||
export { resolveAgentConfig } from "./agent-scope.js";
|
||||
export { AGENT_LANE_SUBAGENT } from "./lanes.js";
|
||||
export { resolveSubagentSpawnModelSelection } from "./model-selection.js";
|
||||
export { resolveSandboxRuntimeStatus } from "./sandbox/runtime-status.js";
|
||||
export { buildSubagentSystemPrompt } from "./subagent-announce.js";
|
||||
export {
|
||||
resolveDisplaySessionKey,
|
||||
resolveInternalSessionKey,
|
||||
resolveMainSessionAlias,
|
||||
} from "./tools/sessions-helpers.js";
|
||||
@@ -122,117 +122,62 @@ export async function loadSubagentSpawnModuleForTest(params: {
|
||||
}) {
|
||||
vi.resetModules();
|
||||
|
||||
vi.doMock("../gateway/call.js", () => ({
|
||||
vi.doMock("./subagent-spawn.runtime.js", () => ({
|
||||
callGateway: (opts: unknown) => params.callGatewayMock(opts),
|
||||
buildSubagentSystemPrompt: () => "system-prompt",
|
||||
getGlobalHookRunner: () => params.hookRunner ?? { hasHooks: () => false },
|
||||
emitSessionLifecycleEvent: (...args: unknown[]) =>
|
||||
params.emitSessionLifecycleEventMock?.(...args),
|
||||
formatThinkingLevels: (levels: string[]) => levels.join(", "),
|
||||
normalizeThinkLevel: (level: unknown) =>
|
||||
typeof level === "string" && level.trim() ? level.trim() : undefined,
|
||||
DEFAULT_SUBAGENT_MAX_SPAWN_DEPTH: 3,
|
||||
ADMIN_SCOPE: "operator.admin",
|
||||
AGENT_LANE_SUBAGENT: "subagent",
|
||||
loadConfig: () =>
|
||||
params.loadConfig?.() ?? createSubagentSpawnTestConfig(params.workspaceDir ?? os.tmpdir()),
|
||||
mergeSessionEntry: (
|
||||
current: Record<string, unknown> | undefined,
|
||||
next: Record<string, unknown>,
|
||||
) => ({
|
||||
...current,
|
||||
...next,
|
||||
}),
|
||||
updateSessionStore:
|
||||
params.updateSessionStoreMock ??
|
||||
(async (_storePath: string, mutator: SessionStoreMutator) => {
|
||||
const store: SessionStore = {};
|
||||
await mutator(store);
|
||||
return store;
|
||||
}),
|
||||
isAdminOnlyMethod: (method: string) =>
|
||||
method === "sessions.patch" || method === "sessions.delete",
|
||||
pruneLegacyStoreKeys: (...args: unknown[]) => params.pruneLegacyStoreKeysMock?.(...args),
|
||||
resolveGatewaySessionStoreTarget: (targetParams: { key: string }) => ({
|
||||
agentId: "main",
|
||||
storePath: params.sessionStorePath ?? "/tmp/subagent-spawn-model-session.json",
|
||||
canonicalKey: targetParams.key,
|
||||
storeKeys: [targetParams.key],
|
||||
}),
|
||||
normalizeDeliveryContext: identityDeliveryContext,
|
||||
resolveAgentConfig: params.resolveAgentConfig ?? (() => undefined),
|
||||
resolveAgentWorkspaceDir:
|
||||
params.resolveAgentWorkspaceDir ?? (() => params.workspaceDir ?? os.tmpdir()),
|
||||
resolveSubagentSpawnModelSelection:
|
||||
params.resolveSubagentSpawnModelSelection ??
|
||||
((spawnParams: { modelOverride?: unknown }) =>
|
||||
typeof spawnParams.modelOverride === "string" && spawnParams.modelOverride.trim()
|
||||
? spawnParams.modelOverride.trim()
|
||||
: "openai/gpt-4"),
|
||||
resolveSandboxRuntimeStatus:
|
||||
params.resolveSandboxRuntimeStatus ?? (() => ({ sandboxed: false })),
|
||||
...createDefaultSessionHelperMocks(),
|
||||
}));
|
||||
|
||||
vi.doMock("../config/config.js", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("../config/config.js")>();
|
||||
return {
|
||||
...actual,
|
||||
loadConfig: () =>
|
||||
params.loadConfig?.() ?? createSubagentSpawnTestConfig(params.workspaceDir ?? os.tmpdir()),
|
||||
};
|
||||
});
|
||||
|
||||
if (params.updateSessionStoreMock) {
|
||||
vi.doMock("../config/sessions.js", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("../config/sessions.js")>();
|
||||
return {
|
||||
...actual,
|
||||
updateSessionStore: (...args: unknown[]) => params.updateSessionStoreMock?.(...args),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
if (params.pruneLegacyStoreKeysMock) {
|
||||
vi.doMock("../gateway/session-utils.js", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("../gateway/session-utils.js")>();
|
||||
return {
|
||||
...actual,
|
||||
resolveGatewaySessionStoreTarget: (targetParams: { key: string }) => ({
|
||||
agentId: "main",
|
||||
storePath: params.sessionStorePath ?? "/tmp/subagent-spawn-model-session.json",
|
||||
canonicalKey: targetParams.key,
|
||||
storeKeys: [targetParams.key],
|
||||
}),
|
||||
pruneLegacyStoreKeys: (...args: unknown[]) => params.pruneLegacyStoreKeysMock?.(...args),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
if (params.emitSessionLifecycleEventMock) {
|
||||
vi.doMock("../sessions/session-lifecycle-events.js", async (importOriginal) => {
|
||||
const actual =
|
||||
await importOriginal<typeof import("../sessions/session-lifecycle-events.js")>();
|
||||
return {
|
||||
...actual,
|
||||
emitSessionLifecycleEvent: (...args: unknown[]) =>
|
||||
params.emitSessionLifecycleEventMock?.(...args),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
vi.doMock("./subagent-announce.js", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("./subagent-announce.js")>();
|
||||
return {
|
||||
...actual,
|
||||
buildSubagentSystemPrompt: () => "system-prompt",
|
||||
};
|
||||
});
|
||||
|
||||
vi.doMock("./agent-scope.js", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("./agent-scope.js")>();
|
||||
return {
|
||||
...actual,
|
||||
resolveAgentConfig: params.resolveAgentConfig ?? actual.resolveAgentConfig,
|
||||
resolveAgentWorkspaceDir:
|
||||
params.resolveAgentWorkspaceDir ?? (() => params.workspaceDir ?? os.tmpdir()),
|
||||
};
|
||||
});
|
||||
|
||||
vi.doMock("./subagent-depth.js", () => ({
|
||||
getSubagentDepthFromSessionStore: () => 0,
|
||||
}));
|
||||
|
||||
vi.doMock("./model-selection.js", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("./model-selection.js")>();
|
||||
return {
|
||||
...actual,
|
||||
resolveSubagentSpawnModelSelection:
|
||||
params.resolveSubagentSpawnModelSelection ?? actual.resolveSubagentSpawnModelSelection,
|
||||
};
|
||||
});
|
||||
|
||||
vi.doMock("./sandbox/runtime-status.js", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("./sandbox/runtime-status.js")>();
|
||||
return {
|
||||
...actual,
|
||||
resolveSandboxRuntimeStatus:
|
||||
params.resolveSandboxRuntimeStatus ?? actual.resolveSandboxRuntimeStatus,
|
||||
};
|
||||
});
|
||||
|
||||
vi.doMock("../plugins/hook-runner-global.js", () => ({
|
||||
getGlobalHookRunner: () => params.hookRunner ?? { hasHooks: () => false },
|
||||
}));
|
||||
|
||||
vi.doMock("../utils/delivery-context.js", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("../utils/delivery-context.js")>();
|
||||
return {
|
||||
...actual,
|
||||
normalizeDeliveryContext: identityDeliveryContext,
|
||||
};
|
||||
});
|
||||
|
||||
vi.doMock("./tools/sessions-helpers.js", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("./tools/sessions-helpers.js")>();
|
||||
return {
|
||||
...actual,
|
||||
...createDefaultSessionHelperMocks(),
|
||||
};
|
||||
});
|
||||
|
||||
const subagentRegistry = await import("./subagent-registry.js");
|
||||
if (params.registerSubagentRunMock) {
|
||||
vi.spyOn(subagentRegistry, "registerSubagentRun").mockImplementation(
|
||||
|
||||
@@ -1,16 +1,5 @@
|
||||
import crypto from "node:crypto";
|
||||
import { promises as fs } from "node:fs";
|
||||
import { formatThinkingLevels, normalizeThinkLevel } from "../auto-reply/thinking.js";
|
||||
import { DEFAULT_SUBAGENT_MAX_SPAWN_DEPTH } from "../config/agent-limits.js";
|
||||
import { loadConfig } from "../config/config.js";
|
||||
import { mergeSessionEntry, updateSessionStore } from "../config/sessions.js";
|
||||
import { callGateway } from "../gateway/call.js";
|
||||
import { ADMIN_SCOPE, isAdminOnlyMethod } from "../gateway/method-scopes.js";
|
||||
import {
|
||||
pruneLegacyStoreKeys,
|
||||
resolveGatewaySessionStoreTarget,
|
||||
} from "../gateway/session-utils.js";
|
||||
import { getGlobalHookRunner } from "../plugins/hook-runner-global.js";
|
||||
import type { SubagentLifecycleHookRunner } from "../plugins/hooks.js";
|
||||
import {
|
||||
isValidAgentId,
|
||||
@@ -18,18 +7,11 @@ import {
|
||||
normalizeAgentId,
|
||||
parseAgentSessionKey,
|
||||
} from "../routing/session-key.js";
|
||||
import { emitSessionLifecycleEvent } from "../sessions/session-lifecycle-events.js";
|
||||
import { normalizeDeliveryContext } from "../utils/delivery-context.js";
|
||||
import { resolveAgentConfig } from "./agent-scope.js";
|
||||
import { AGENT_LANE_SUBAGENT } from "./lanes.js";
|
||||
import { resolveSubagentSpawnModelSelection } from "./model-selection.js";
|
||||
import { resolveSandboxRuntimeStatus } from "./sandbox/runtime-status.js";
|
||||
import {
|
||||
mapToolContextToSpawnedRunMetadata,
|
||||
normalizeSpawnedRunMetadata,
|
||||
resolveSpawnedWorkspaceInheritance,
|
||||
} from "./spawned-context.js";
|
||||
import { buildSubagentSystemPrompt } from "./subagent-announce.js";
|
||||
import {
|
||||
decodeStrictBase64,
|
||||
materializeSubagentAttachments,
|
||||
@@ -38,12 +20,31 @@ import {
|
||||
import { resolveSubagentCapabilities } from "./subagent-capabilities.js";
|
||||
import { getSubagentDepthFromSessionStore } from "./subagent-depth.js";
|
||||
import { countActiveRunsForSession, registerSubagentRun } from "./subagent-registry.js";
|
||||
import { readStringParam } from "./tools/common.js";
|
||||
import {
|
||||
ADMIN_SCOPE,
|
||||
AGENT_LANE_SUBAGENT,
|
||||
DEFAULT_SUBAGENT_MAX_SPAWN_DEPTH,
|
||||
buildSubagentSystemPrompt,
|
||||
callGateway,
|
||||
emitSessionLifecycleEvent,
|
||||
formatThinkingLevels,
|
||||
getGlobalHookRunner,
|
||||
loadConfig,
|
||||
mergeSessionEntry,
|
||||
normalizeDeliveryContext,
|
||||
normalizeThinkLevel,
|
||||
pruneLegacyStoreKeys,
|
||||
resolveAgentConfig,
|
||||
resolveDisplaySessionKey,
|
||||
resolveGatewaySessionStoreTarget,
|
||||
resolveInternalSessionKey,
|
||||
resolveMainSessionAlias,
|
||||
} from "./tools/sessions-helpers.js";
|
||||
resolveSandboxRuntimeStatus,
|
||||
resolveSubagentSpawnModelSelection,
|
||||
updateSessionStore,
|
||||
isAdminOnlyMethod,
|
||||
} from "./subagent-spawn.runtime.js";
|
||||
import { readStringParam } from "./tools/common.js";
|
||||
|
||||
export const SUBAGENT_SPAWN_MODES = ["run", "session"] as const;
|
||||
export type SpawnSubagentMode = (typeof SUBAGENT_SPAWN_MODES)[number];
|
||||
|
||||
Reference in New Issue
Block a user