mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-05 00:00:22 +00:00
refactor: move session lifecycle and outbound fallbacks into plugins
This commit is contained in:
@@ -1,6 +1,9 @@
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { telegramPlugin } from "../../../extensions/telegram/src/channel.js";
|
||||
import type { OpenClawConfig } from "../../config/config.js";
|
||||
import type { SessionBindingRecord } from "../../infra/outbound/session-binding-service.js";
|
||||
import { setActivePluginRegistry } from "../../plugins/runtime.js";
|
||||
import { createTestRegistry } from "../../test-utils/channel-plugins.js";
|
||||
|
||||
const hoisted = vi.hoisted(() => {
|
||||
const getThreadBindingManagerMock = vi.fn();
|
||||
@@ -19,28 +22,34 @@ const hoisted = vi.hoisted(() => {
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("../../../extensions/discord/src/monitor/thread-bindings.js", async (importOriginal) => {
|
||||
const actual =
|
||||
await importOriginal<
|
||||
typeof import("../../../extensions/discord/src/monitor/thread-bindings.js")
|
||||
>();
|
||||
vi.mock("../../plugins/runtime/index.js", async () => {
|
||||
const discordThreadBindings = await vi.importActual<
|
||||
typeof import("../../../extensions/discord/src/monitor/thread-bindings.js")
|
||||
>("../../../extensions/discord/src/monitor/thread-bindings.js");
|
||||
return {
|
||||
...actual,
|
||||
getThreadBindingManager: hoisted.getThreadBindingManagerMock,
|
||||
setThreadBindingIdleTimeoutBySessionKey: hoisted.setThreadBindingIdleTimeoutBySessionKeyMock,
|
||||
setThreadBindingMaxAgeBySessionKey: hoisted.setThreadBindingMaxAgeBySessionKeyMock,
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("../../../extensions/telegram/src/thread-bindings.js", async (importOriginal) => {
|
||||
const actual =
|
||||
await importOriginal<typeof import("../../../extensions/telegram/src/thread-bindings.js")>();
|
||||
return {
|
||||
...actual,
|
||||
setTelegramThreadBindingIdleTimeoutBySessionKey:
|
||||
hoisted.setTelegramThreadBindingIdleTimeoutBySessionKeyMock,
|
||||
setTelegramThreadBindingMaxAgeBySessionKey:
|
||||
hoisted.setTelegramThreadBindingMaxAgeBySessionKeyMock,
|
||||
createPluginRuntime: () => ({
|
||||
channel: {
|
||||
discord: {
|
||||
threadBindings: {
|
||||
getManager: hoisted.getThreadBindingManagerMock,
|
||||
resolveIdleTimeoutMs: discordThreadBindings.resolveThreadBindingIdleTimeoutMs,
|
||||
resolveInactivityExpiresAt:
|
||||
discordThreadBindings.resolveThreadBindingInactivityExpiresAt,
|
||||
resolveMaxAgeMs: discordThreadBindings.resolveThreadBindingMaxAgeMs,
|
||||
resolveMaxAgeExpiresAt: discordThreadBindings.resolveThreadBindingMaxAgeExpiresAt,
|
||||
setIdleTimeoutBySessionKey: hoisted.setThreadBindingIdleTimeoutBySessionKeyMock,
|
||||
setMaxAgeBySessionKey: hoisted.setThreadBindingMaxAgeBySessionKeyMock,
|
||||
unbindBySessionKey: vi.fn(),
|
||||
},
|
||||
},
|
||||
telegram: {
|
||||
threadBindings: {
|
||||
setIdleTimeoutBySessionKey: hoisted.setTelegramThreadBindingIdleTimeoutBySessionKeyMock,
|
||||
setMaxAgeBySessionKey: hoisted.setTelegramThreadBindingMaxAgeBySessionKeyMock,
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
@@ -168,6 +177,9 @@ function createFakeThreadBindingManager(binding: FakeBinding | null) {
|
||||
|
||||
describe("/session idle and /session max-age", () => {
|
||||
beforeEach(() => {
|
||||
setActivePluginRegistry(
|
||||
createTestRegistry([{ pluginId: "telegram", source: "test", plugin: telegramPlugin }]),
|
||||
);
|
||||
hoisted.getThreadBindingManagerMock.mockReset();
|
||||
hoisted.setThreadBindingIdleTimeoutBySessionKeyMock.mockReset();
|
||||
hoisted.setThreadBindingMaxAgeBySessionKeyMock.mockReset();
|
||||
|
||||
@@ -1,18 +1,5 @@
|
||||
import {
|
||||
formatThreadBindingDurationLabel,
|
||||
getThreadBindingManager,
|
||||
resolveThreadBindingIdleTimeoutMs,
|
||||
resolveThreadBindingInactivityExpiresAt,
|
||||
resolveThreadBindingMaxAgeExpiresAt,
|
||||
resolveThreadBindingMaxAgeMs,
|
||||
setThreadBindingIdleTimeoutBySessionKey,
|
||||
setThreadBindingMaxAgeBySessionKey,
|
||||
} from "../../../extensions/discord/src/monitor/thread-bindings.js";
|
||||
import {
|
||||
setTelegramThreadBindingIdleTimeoutBySessionKey,
|
||||
setTelegramThreadBindingMaxAgeBySessionKey,
|
||||
} from "../../../extensions/telegram/src/thread-bindings.js";
|
||||
import { resolveFastModeState } from "../../agents/fast-mode.js";
|
||||
import { formatThreadBindingDurationLabel } from "../../channels/thread-bindings-messages.js";
|
||||
import { parseDurationMs } from "../../cli/parse-duration.js";
|
||||
import { isRestartEnabled } from "../../config/commands.js";
|
||||
import { logVerbose } from "../../globals.js";
|
||||
@@ -20,6 +7,7 @@ import { getSessionBindingService } from "../../infra/outbound/session-binding-s
|
||||
import type { SessionBindingRecord } from "../../infra/outbound/session-binding-service.js";
|
||||
import { scheduleGatewaySigusr1Restart, triggerOpenClawRestart } from "../../infra/restart.js";
|
||||
import { loadCostUsageSummary, loadSessionCostSummary } from "../../infra/session-cost-usage.js";
|
||||
import { createPluginRuntime } from "../../plugins/runtime/index.js";
|
||||
import { formatTokenCount, formatUsd } from "../../utils/usage-format.js";
|
||||
import { parseActivationCommand } from "../group-activation.js";
|
||||
import { parseSendPolicyCommand } from "../send-policy.js";
|
||||
@@ -34,6 +22,7 @@ const SESSION_COMMAND_PREFIX = "/session";
|
||||
const SESSION_DURATION_OFF_VALUES = new Set(["off", "disable", "disabled", "none", "0"]);
|
||||
const SESSION_ACTION_IDLE = "idle";
|
||||
const SESSION_ACTION_MAX_AGE = "max-age";
|
||||
const channelRuntime = createPluginRuntime().channel;
|
||||
|
||||
function resolveSessionCommandUsage() {
|
||||
return "Usage: /session idle <duration|off> | /session max-age <duration|off> (example: /session idle 24h)";
|
||||
@@ -385,7 +374,9 @@ export const handleSessionCommand: CommandHandler = async (params, allowTextComm
|
||||
params.ctx.MessageThreadId != null ? String(params.ctx.MessageThreadId).trim() : "";
|
||||
const telegramConversationId = onTelegram ? resolveTelegramConversationId(params) : undefined;
|
||||
|
||||
const discordManager = onDiscord ? getThreadBindingManager(accountId) : null;
|
||||
const discordManager = onDiscord
|
||||
? channelRuntime.discord.threadBindings.getManager(accountId)
|
||||
: null;
|
||||
if (onDiscord && !discordManager) {
|
||||
return {
|
||||
shouldContinue: false,
|
||||
@@ -433,13 +424,13 @@ export const handleSessionCommand: CommandHandler = async (params, allowTextComm
|
||||
}
|
||||
|
||||
const idleTimeoutMs = onDiscord
|
||||
? resolveThreadBindingIdleTimeoutMs({
|
||||
? channelRuntime.discord.threadBindings.resolveIdleTimeoutMs({
|
||||
record: discordBinding!,
|
||||
defaultIdleTimeoutMs: discordManager!.getIdleTimeoutMs(),
|
||||
})
|
||||
: resolveTelegramBindingDurationMs(telegramBinding!, "idleTimeoutMs", 24 * 60 * 60 * 1000);
|
||||
const idleExpiresAt = onDiscord
|
||||
? resolveThreadBindingInactivityExpiresAt({
|
||||
? channelRuntime.discord.threadBindings.resolveInactivityExpiresAt({
|
||||
record: discordBinding!,
|
||||
defaultIdleTimeoutMs: discordManager!.getIdleTimeoutMs(),
|
||||
})
|
||||
@@ -447,13 +438,13 @@ export const handleSessionCommand: CommandHandler = async (params, allowTextComm
|
||||
? resolveTelegramBindingLastActivityAt(telegramBinding!) + idleTimeoutMs
|
||||
: undefined;
|
||||
const maxAgeMs = onDiscord
|
||||
? resolveThreadBindingMaxAgeMs({
|
||||
? channelRuntime.discord.threadBindings.resolveMaxAgeMs({
|
||||
record: discordBinding!,
|
||||
defaultMaxAgeMs: discordManager!.getMaxAgeMs(),
|
||||
})
|
||||
: resolveTelegramBindingDurationMs(telegramBinding!, "maxAgeMs", 0);
|
||||
const maxAgeExpiresAt = onDiscord
|
||||
? resolveThreadBindingMaxAgeExpiresAt({
|
||||
? channelRuntime.discord.threadBindings.resolveMaxAgeExpiresAt({
|
||||
record: discordBinding!,
|
||||
defaultMaxAgeMs: discordManager!.getMaxAgeMs(),
|
||||
})
|
||||
@@ -528,24 +519,24 @@ export const handleSessionCommand: CommandHandler = async (params, allowTextComm
|
||||
const updatedBindings = (() => {
|
||||
if (onDiscord) {
|
||||
return action === SESSION_ACTION_IDLE
|
||||
? setThreadBindingIdleTimeoutBySessionKey({
|
||||
? channelRuntime.discord.threadBindings.setIdleTimeoutBySessionKey({
|
||||
targetSessionKey: discordBinding!.targetSessionKey,
|
||||
accountId,
|
||||
idleTimeoutMs: durationMs,
|
||||
})
|
||||
: setThreadBindingMaxAgeBySessionKey({
|
||||
: channelRuntime.discord.threadBindings.setMaxAgeBySessionKey({
|
||||
targetSessionKey: discordBinding!.targetSessionKey,
|
||||
accountId,
|
||||
maxAgeMs: durationMs,
|
||||
});
|
||||
}
|
||||
return action === SESSION_ACTION_IDLE
|
||||
? setTelegramThreadBindingIdleTimeoutBySessionKey({
|
||||
? channelRuntime.telegram.threadBindings.setIdleTimeoutBySessionKey({
|
||||
targetSessionKey: telegramBinding!.targetSessionKey,
|
||||
accountId,
|
||||
idleTimeoutMs: durationMs,
|
||||
})
|
||||
: setTelegramThreadBindingMaxAgeBySessionKey({
|
||||
: channelRuntime.telegram.threadBindings.setMaxAgeBySessionKey({
|
||||
targetSessionKey: telegramBinding!.targetSessionKey,
|
||||
accountId,
|
||||
maxAgeMs: durationMs,
|
||||
|
||||
Reference in New Issue
Block a user