mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 07:30:43 +00:00
fix(gateway): default restart acknowledgement continuations
This commit is contained in:
@@ -141,4 +141,30 @@ describe("gateway tool restart continuation", () => {
|
||||
});
|
||||
expect(result?.details).toEqual({ scheduled: true, delayMs: 250 });
|
||||
});
|
||||
|
||||
it("defaults session-scoped restarts to a success continuation", async () => {
|
||||
const { createGatewayTool } = await import("./gateway-tool.js");
|
||||
const { DEFAULT_RESTART_SUCCESS_CONTINUATION_MESSAGE } =
|
||||
await import("../../infra/restart-sentinel.js");
|
||||
const tool = createGatewayTool({
|
||||
agentSessionKey: "agent:main:main",
|
||||
config: {},
|
||||
});
|
||||
|
||||
await tool.execute?.("tool-call-1", {
|
||||
action: "restart",
|
||||
delayMs: 250,
|
||||
reason: "restart requested",
|
||||
});
|
||||
|
||||
expect(writeRestartSentinelMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
sessionKey: "agent:main:main",
|
||||
continuation: {
|
||||
kind: "agentTurn",
|
||||
message: DEFAULT_RESTART_SUCCESS_CONTINUATION_MESSAGE,
|
||||
},
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -6,6 +6,7 @@ import { applyMergePatch } from "../../config/merge-patch.js";
|
||||
import { extractDeliveryInfo } from "../../config/sessions.js";
|
||||
import type { OpenClawConfig } from "../../config/types.openclaw.js";
|
||||
import {
|
||||
buildRestartSuccessContinuation,
|
||||
formatDoctorNonInteractiveHint,
|
||||
type RestartSentinelPayload,
|
||||
writeRestartSentinel,
|
||||
@@ -350,17 +351,11 @@ export function createGatewayTool(opts?: {
|
||||
deliveryContext,
|
||||
threadId,
|
||||
message: note ?? reason ?? null,
|
||||
continuation: continuationMessage
|
||||
? continuationKind === "systemEvent"
|
||||
? {
|
||||
kind: "systemEvent",
|
||||
text: continuationMessage,
|
||||
}
|
||||
: {
|
||||
kind: "agentTurn",
|
||||
message: continuationMessage,
|
||||
}
|
||||
: null,
|
||||
continuation: buildRestartSuccessContinuation({
|
||||
sessionKey,
|
||||
continuationKind,
|
||||
continuationMessage,
|
||||
}),
|
||||
doctorHint: formatDoctorNonInteractiveHint(),
|
||||
stats: {
|
||||
mode: "gateway.restart",
|
||||
|
||||
136
src/auto-reply/reply/commands-session-restart.test.ts
Normal file
136
src/auto-reply/reply/commands-session-restart.test.ts
Normal file
@@ -0,0 +1,136 @@
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import type { RestartSentinelPayload } from "../../infra/restart-sentinel.js";
|
||||
import type { HandleCommandsParams } from "./commands-types.js";
|
||||
|
||||
const mocks = vi.hoisted(() => ({
|
||||
isRestartEnabled: vi.fn(() => true),
|
||||
extractDeliveryInfo: vi.fn(() => ({
|
||||
deliveryContext: {
|
||||
channel: "telegram",
|
||||
to: "telegram:123",
|
||||
accountId: "default",
|
||||
},
|
||||
threadId: "thread-1",
|
||||
})),
|
||||
formatDoctorNonInteractiveHint: vi.fn(() => "Run: openclaw doctor --non-interactive"),
|
||||
writeRestartSentinel: vi.fn(async (_payload: RestartSentinelPayload) => "/tmp/sentinel.json"),
|
||||
scheduleGatewaySigusr1Restart: vi.fn(() => ({ scheduled: true })),
|
||||
triggerOpenClawRestart: vi.fn(() => ({ ok: true, method: "launchctl" })),
|
||||
}));
|
||||
|
||||
vi.mock("../../config/commands.flags.js", () => ({
|
||||
isRestartEnabled: mocks.isRestartEnabled,
|
||||
}));
|
||||
|
||||
vi.mock("../../config/sessions.js", () => ({
|
||||
extractDeliveryInfo: mocks.extractDeliveryInfo,
|
||||
}));
|
||||
|
||||
vi.mock("../../globals.js", () => ({
|
||||
logVerbose: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("../../channels/plugins/index.js", () => ({
|
||||
getChannelPlugin: vi.fn(),
|
||||
normalizeChannelId: (value?: string | null) => value?.trim().toLowerCase() ?? null,
|
||||
}));
|
||||
|
||||
vi.mock("../../channels/plugins/conversation-bindings.js", () => ({
|
||||
setChannelConversationBindingIdleTimeoutBySessionKey: vi.fn(),
|
||||
setChannelConversationBindingMaxAgeBySessionKey: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("../../infra/outbound/session-binding-service.js", () => ({
|
||||
getSessionBindingService: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("../../infra/restart-sentinel.js", async () => {
|
||||
const actual = await vi.importActual<typeof import("../../infra/restart-sentinel.js")>(
|
||||
"../../infra/restart-sentinel.js",
|
||||
);
|
||||
return {
|
||||
...actual,
|
||||
formatDoctorNonInteractiveHint: mocks.formatDoctorNonInteractiveHint,
|
||||
writeRestartSentinel: mocks.writeRestartSentinel,
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("../../infra/restart.js", () => ({
|
||||
scheduleGatewaySigusr1Restart: mocks.scheduleGatewaySigusr1Restart,
|
||||
triggerOpenClawRestart: mocks.triggerOpenClawRestart,
|
||||
}));
|
||||
|
||||
const { handleRestartCommand } = await import("./commands-session.js");
|
||||
|
||||
function restartCommandParams(overrides?: Partial<HandleCommandsParams>): HandleCommandsParams {
|
||||
return {
|
||||
ctx: {},
|
||||
cfg: {},
|
||||
command: {
|
||||
surface: "telegram",
|
||||
channel: "telegram",
|
||||
ownerList: [],
|
||||
senderIsOwner: true,
|
||||
isAuthorizedSender: true,
|
||||
senderId: "user-1",
|
||||
rawBodyNormalized: "/restart",
|
||||
commandBodyNormalized: "/restart",
|
||||
from: "telegram:123",
|
||||
to: "bot",
|
||||
},
|
||||
directives: {},
|
||||
elevated: { enabled: true, allowed: true, failures: [] },
|
||||
sessionKey: "agent:main:telegram:direct:123:thread:thread-1",
|
||||
workspaceDir: "/tmp",
|
||||
defaultGroupActivation: () => "mention",
|
||||
resolvedVerboseLevel: "off",
|
||||
resolvedReasoningLevel: "off",
|
||||
resolveDefaultThinkingLevel: async () => undefined,
|
||||
provider: "openai",
|
||||
model: "gpt-5.4",
|
||||
contextTokens: 0,
|
||||
isGroup: false,
|
||||
...overrides,
|
||||
} as HandleCommandsParams;
|
||||
}
|
||||
|
||||
describe("handleRestartCommand", () => {
|
||||
beforeEach(() => {
|
||||
mocks.isRestartEnabled.mockReset();
|
||||
mocks.isRestartEnabled.mockReturnValue(true);
|
||||
mocks.extractDeliveryInfo.mockClear();
|
||||
mocks.formatDoctorNonInteractiveHint.mockClear();
|
||||
mocks.writeRestartSentinel.mockClear();
|
||||
mocks.scheduleGatewaySigusr1Restart.mockClear();
|
||||
mocks.triggerOpenClawRestart.mockReset();
|
||||
mocks.triggerOpenClawRestart.mockReturnValue({ ok: true, method: "launchctl" });
|
||||
});
|
||||
|
||||
it("writes a routed restart sentinel before restarting from chat", async () => {
|
||||
const { DEFAULT_RESTART_SUCCESS_CONTINUATION_MESSAGE } =
|
||||
await import("../../infra/restart-sentinel.js");
|
||||
|
||||
const result = await handleRestartCommand(restartCommandParams(), true);
|
||||
|
||||
expect(result?.shouldContinue).toBe(false);
|
||||
expect(mocks.writeRestartSentinel).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
kind: "restart",
|
||||
status: "ok",
|
||||
sessionKey: "agent:main:telegram:direct:123:thread:thread-1",
|
||||
deliveryContext: {
|
||||
channel: "telegram",
|
||||
to: "telegram:123",
|
||||
accountId: "default",
|
||||
},
|
||||
threadId: "thread-1",
|
||||
message: "/restart",
|
||||
continuation: {
|
||||
kind: "agentTurn",
|
||||
message: DEFAULT_RESTART_SUCCESS_CONTINUATION_MESSAGE,
|
||||
},
|
||||
}),
|
||||
);
|
||||
expect(mocks.triggerOpenClawRestart).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
@@ -8,9 +8,16 @@ import { getChannelPlugin, normalizeChannelId } from "../../channels/plugins/ind
|
||||
import { formatThreadBindingDurationLabel } from "../../channels/thread-bindings-messages.js";
|
||||
import { parseDurationMs } from "../../cli/parse-duration.js";
|
||||
import { isRestartEnabled } from "../../config/commands.flags.js";
|
||||
import { extractDeliveryInfo } from "../../config/sessions.js";
|
||||
import { logVerbose } from "../../globals.js";
|
||||
import { getSessionBindingService } from "../../infra/outbound/session-binding-service.js";
|
||||
import type { SessionBindingRecord } from "../../infra/outbound/session-binding-service.js";
|
||||
import {
|
||||
buildRestartSuccessContinuation,
|
||||
formatDoctorNonInteractiveHint,
|
||||
type RestartSentinelPayload,
|
||||
writeRestartSentinel,
|
||||
} from "../../infra/restart-sentinel.js";
|
||||
import { scheduleGatewaySigusr1Restart, triggerOpenClawRestart } from "../../infra/restart.js";
|
||||
import { loadCostUsageSummary, loadSessionCostSummary } from "../../infra/session-cost-usage.js";
|
||||
import {
|
||||
@@ -26,7 +33,7 @@ import { resolveCommandSurfaceChannel } from "./channel-context.js";
|
||||
import { rejectNonOwnerCommand, rejectUnauthorizedCommand } from "./command-gates.js";
|
||||
import { handleAbortTrigger, handleStopCommand } from "./commands-session-abort.js";
|
||||
import { persistSessionEntry } from "./commands-session-store.js";
|
||||
import type { CommandHandler } from "./commands-types.js";
|
||||
import type { CommandHandler, HandleCommandsParams } from "./commands-types.js";
|
||||
import { resolveConversationBindingContextFromAcpCommand } from "./conversation-binding-input.js";
|
||||
|
||||
const SESSION_COMMAND_PREFIX = "/session";
|
||||
@@ -34,6 +41,30 @@ const SESSION_DURATION_OFF_VALUES = new Set(["off", "disable", "disabled", "none
|
||||
const SESSION_ACTION_IDLE = "idle";
|
||||
const SESSION_ACTION_MAX_AGE = "max-age";
|
||||
|
||||
async function writeRestartCommandSentinel(params: HandleCommandsParams) {
|
||||
const sessionKey = normalizeOptionalString(params.sessionKey);
|
||||
if (!sessionKey) {
|
||||
return;
|
||||
}
|
||||
const { deliveryContext, threadId } = extractDeliveryInfo(sessionKey);
|
||||
const payload: RestartSentinelPayload = {
|
||||
kind: "restart",
|
||||
status: "ok",
|
||||
ts: Date.now(),
|
||||
sessionKey,
|
||||
deliveryContext,
|
||||
threadId,
|
||||
message: "/restart",
|
||||
continuation: buildRestartSuccessContinuation({ sessionKey }),
|
||||
doctorHint: formatDoctorNonInteractiveHint(),
|
||||
stats: {
|
||||
mode: "gateway.restart",
|
||||
reason: "/restart",
|
||||
},
|
||||
};
|
||||
await writeRestartSentinel(payload).catch(() => {});
|
||||
}
|
||||
|
||||
function resolveSessionCommandUsage() {
|
||||
return "Usage: /session idle <duration|off> | /session max-age <duration|off> (example: /session idle 24h)";
|
||||
}
|
||||
@@ -641,6 +672,7 @@ export const handleRestartCommand: CommandHandler = async (params, allowTextComm
|
||||
}
|
||||
const hasSigusr1Listener = process.listenerCount("SIGUSR1") > 0;
|
||||
if (hasSigusr1Listener) {
|
||||
await writeRestartCommandSentinel(params);
|
||||
scheduleGatewaySigusr1Restart({ reason: "/restart" });
|
||||
return {
|
||||
shouldContinue: false,
|
||||
@@ -649,6 +681,7 @@ export const handleRestartCommand: CommandHandler = async (params, allowTextComm
|
||||
},
|
||||
};
|
||||
}
|
||||
await writeRestartCommandSentinel(params);
|
||||
const restartMethod = triggerOpenClawRestart();
|
||||
if (!restartMethod.ok) {
|
||||
const detail = restartMethod.detail ? ` Details: ${restartMethod.detail}` : "";
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import type { OpenClawConfig } from "../../config/types.openclaw.js";
|
||||
import type { RestartSentinelPayload } from "../../infra/restart-sentinel.js";
|
||||
import {
|
||||
createConfigHandlerHarness,
|
||||
createConfigWriteSnapshot,
|
||||
@@ -15,6 +16,11 @@ const scheduleGatewaySigusr1RestartMock = vi.fn(() => ({
|
||||
delayMs: 1_000,
|
||||
coalesced: false,
|
||||
}));
|
||||
const restartSentinelMocks = vi.hoisted(() => ({
|
||||
writeRestartSentinel: vi.fn(async (_payload: RestartSentinelPayload) => {
|
||||
return "/tmp/restart-sentinel.json";
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock("../../config/config.js", async () => {
|
||||
const actual =
|
||||
@@ -40,6 +46,16 @@ vi.mock("../../infra/restart.js", () => ({
|
||||
scheduleGatewaySigusr1Restart: scheduleGatewaySigusr1RestartMock,
|
||||
}));
|
||||
|
||||
vi.mock("../../infra/restart-sentinel.js", async () => {
|
||||
const actual = await vi.importActual<typeof import("../../infra/restart-sentinel.js")>(
|
||||
"../../infra/restart-sentinel.js",
|
||||
);
|
||||
return {
|
||||
...actual,
|
||||
writeRestartSentinel: restartSentinelMocks.writeRestartSentinel,
|
||||
};
|
||||
});
|
||||
|
||||
const { configHandlers } = await import("./config.js");
|
||||
|
||||
afterEach(() => {
|
||||
@@ -52,6 +68,7 @@ beforeEach(() => {
|
||||
config,
|
||||
}));
|
||||
prepareSecretsRuntimeSnapshotMock.mockResolvedValue(undefined);
|
||||
restartSentinelMocks.writeRestartSentinel.mockClear();
|
||||
});
|
||||
|
||||
describe("config shared auth disconnects", () => {
|
||||
@@ -168,4 +185,39 @@ describe("config shared auth disconnects", () => {
|
||||
|
||||
expect(scheduleGatewaySigusr1RestartMock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("adds a default continuation to session-scoped restart sentinels", async () => {
|
||||
const { DEFAULT_RESTART_SUCCESS_CONTINUATION_MESSAGE } =
|
||||
await import("../../infra/restart-sentinel.js");
|
||||
const prevConfig: OpenClawConfig = {
|
||||
gateway: {
|
||||
reload: {
|
||||
mode: "hot",
|
||||
},
|
||||
},
|
||||
};
|
||||
readConfigFileSnapshotForWriteMock.mockResolvedValue(createConfigWriteSnapshot(prevConfig));
|
||||
|
||||
const { options } = createConfigHandlerHarness({
|
||||
method: "config.patch",
|
||||
params: {
|
||||
baseHash: "base-hash",
|
||||
raw: JSON.stringify({ gateway: { port: 19001 } }),
|
||||
restartDelayMs: 1_000,
|
||||
sessionKey: "agent:main:main",
|
||||
},
|
||||
});
|
||||
|
||||
await configHandlers["config.patch"](options);
|
||||
|
||||
expect(restartSentinelMocks.writeRestartSentinel).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
sessionKey: "agent:main:main",
|
||||
continuation: {
|
||||
kind: "agentTurn",
|
||||
message: DEFAULT_RESTART_SUCCESS_CONTINUATION_MESSAGE,
|
||||
},
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -22,6 +22,7 @@ import { extractDeliveryInfo } from "../../config/sessions.js";
|
||||
import type { ConfigValidationIssue, OpenClawConfig } from "../../config/types.openclaw.js";
|
||||
import { formatErrorMessage } from "../../infra/errors.js";
|
||||
import {
|
||||
buildRestartSuccessContinuation,
|
||||
formatDoctorNonInteractiveHint,
|
||||
type RestartSentinelPayload,
|
||||
writeRestartSentinel,
|
||||
@@ -367,6 +368,7 @@ function buildConfigRestartSentinelPayload(params: {
|
||||
deliveryContext: params.deliveryContext,
|
||||
threadId: params.threadId,
|
||||
message: params.note ?? null,
|
||||
continuation: buildRestartSuccessContinuation({ sessionKey: params.sessionKey }),
|
||||
doctorHint: formatDoctorNonInteractiveHint(),
|
||||
stats: {
|
||||
mode: params.mode,
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import type { RestartSentinelPayload } from "../../infra/restart-sentinel.js";
|
||||
import {
|
||||
DEFAULT_RESTART_SUCCESS_CONTINUATION_MESSAGE,
|
||||
type RestartSentinelPayload,
|
||||
} from "../../infra/restart-sentinel.js";
|
||||
import type { UpdateRunResult } from "../../infra/update-runner.js";
|
||||
|
||||
// Capture the sentinel payload written during update.run
|
||||
@@ -122,6 +125,10 @@ describe("update.run sentinel deliveryContext", () => {
|
||||
to: "webchat:user-123",
|
||||
accountId: "default",
|
||||
});
|
||||
expect(capturedPayload!.continuation).toEqual({
|
||||
kind: "agentTurn",
|
||||
message: DEFAULT_RESTART_SUCCESS_CONTINUATION_MESSAGE,
|
||||
});
|
||||
});
|
||||
|
||||
it("omits deliveryContext when no sessionKey is provided", async () => {
|
||||
@@ -132,6 +139,7 @@ describe("update.run sentinel deliveryContext", () => {
|
||||
expect(capturedPayload).toBeDefined();
|
||||
expect(capturedPayload!.deliveryContext).toBeUndefined();
|
||||
expect(capturedPayload!.threadId).toBeUndefined();
|
||||
expect(capturedPayload!.continuation).toBeNull();
|
||||
});
|
||||
|
||||
it("includes threadId in sentinel payload for threaded sessions", async () => {
|
||||
@@ -146,6 +154,10 @@ describe("update.run sentinel deliveryContext", () => {
|
||||
accountId: "workspace-1",
|
||||
});
|
||||
expect(capturedPayload!.threadId).toBe("1234567890.123456");
|
||||
expect(capturedPayload!.continuation).toEqual({
|
||||
kind: "agentTurn",
|
||||
message: DEFAULT_RESTART_SUCCESS_CONTINUATION_MESSAGE,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -194,5 +206,6 @@ describe("update.run restart scheduling", () => {
|
||||
expect(scheduleGatewaySigusr1RestartMock).not.toHaveBeenCalled();
|
||||
expect(payload?.ok).toBe(false);
|
||||
expect(payload?.restart).toBeNull();
|
||||
expect(capturedPayload?.continuation).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,6 +2,7 @@ import { loadConfig } from "../../config/config.js";
|
||||
import { extractDeliveryInfo } from "../../config/sessions.js";
|
||||
import { resolveOpenClawPackageRoot } from "../../infra/openclaw-root.js";
|
||||
import {
|
||||
buildRestartSuccessContinuation,
|
||||
formatDoctorNonInteractiveHint,
|
||||
type RestartSentinelPayload,
|
||||
writeRestartSentinel,
|
||||
@@ -72,6 +73,7 @@ export const updateHandlers: GatewayRequestHandlers = {
|
||||
deliveryContext,
|
||||
threadId,
|
||||
message: note ?? null,
|
||||
continuation: result.status === "ok" ? buildRestartSuccessContinuation({ sessionKey }) : null,
|
||||
doctorHint: formatDoctorNonInteractiveHint(),
|
||||
stats: {
|
||||
mode: result.mode,
|
||||
|
||||
@@ -4,6 +4,8 @@ import { describe, expect, it } from "vitest";
|
||||
import { withTempDir } from "../test-helpers/temp-dir.js";
|
||||
import { captureEnv } from "../test-utils/env.js";
|
||||
import {
|
||||
DEFAULT_RESTART_SUCCESS_CONTINUATION_MESSAGE,
|
||||
buildRestartSuccessContinuation,
|
||||
consumeRestartSentinel,
|
||||
formatDoctorNonInteractiveHint,
|
||||
formatRestartSentinelMessage,
|
||||
@@ -184,6 +186,32 @@ describe("restart sentinel", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("restart success continuation", () => {
|
||||
it("builds the default agent turn for session-scoped restarts", () => {
|
||||
expect(buildRestartSuccessContinuation({ sessionKey: "agent:main:main" })).toEqual({
|
||||
kind: "agentTurn",
|
||||
message: DEFAULT_RESTART_SUCCESS_CONTINUATION_MESSAGE,
|
||||
});
|
||||
});
|
||||
|
||||
it("keeps explicit continuation messages", () => {
|
||||
expect(
|
||||
buildRestartSuccessContinuation({
|
||||
sessionKey: "agent:main:main",
|
||||
continuationKind: "systemEvent",
|
||||
continuationMessage: "wake after restart",
|
||||
}),
|
||||
).toEqual({
|
||||
kind: "systemEvent",
|
||||
text: "wake after restart",
|
||||
});
|
||||
});
|
||||
|
||||
it("stays silent without session context", () => {
|
||||
expect(buildRestartSuccessContinuation({})).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe("restart sentinel message dedup", () => {
|
||||
it("omits duplicate Reason: line when stats.reason matches message", () => {
|
||||
const payload = {
|
||||
|
||||
@@ -62,6 +62,9 @@ export type RestartSentinel = {
|
||||
payload: RestartSentinelPayload;
|
||||
};
|
||||
|
||||
export const DEFAULT_RESTART_SUCCESS_CONTINUATION_MESSAGE =
|
||||
"The gateway restart completed successfully. Tell the user OpenClaw restarted successfully and continue any pending work.";
|
||||
|
||||
const SENTINEL_FILENAME = "restart-sentinel.json";
|
||||
|
||||
export function formatDoctorNonInteractiveHint(
|
||||
@@ -84,6 +87,22 @@ export async function writeRestartSentinel(
|
||||
return filePath;
|
||||
}
|
||||
|
||||
export function buildRestartSuccessContinuation(params: {
|
||||
sessionKey?: string;
|
||||
continuationKind?: string | null;
|
||||
continuationMessage?: string | null;
|
||||
}): RestartSentinelContinuation | null {
|
||||
const message = params.continuationMessage?.trim();
|
||||
if (message) {
|
||||
return params.continuationKind === "systemEvent"
|
||||
? { kind: "systemEvent", text: message }
|
||||
: { kind: "agentTurn", message };
|
||||
}
|
||||
return params.sessionKey?.trim()
|
||||
? { kind: "agentTurn", message: DEFAULT_RESTART_SUCCESS_CONTINUATION_MESSAGE }
|
||||
: null;
|
||||
}
|
||||
|
||||
export async function readRestartSentinel(
|
||||
env: NodeJS.ProcessEnv = process.env,
|
||||
): Promise<RestartSentinel | null> {
|
||||
|
||||
Reference in New Issue
Block a user