mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 05:20:43 +00:00
This commit is contained in:
@@ -14,6 +14,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Channels/Discord: bound message read/search REST calls, route those actions through Gateway execution, and fall back to `CommandTargetSessionKey` for inbound hook session keys so Discord reads do not hang and hooks still fire when `SessionKey` is empty. Fixes #73431. (#73521) Thanks @amknight.
|
||||
- Plugins/media: auto-enable provider plugins referenced by `agents.defaults.imageGenerationModel`, `videoGenerationModel`, and `musicGenerationModel` primary/fallback refs, so configured Google and MiniMax media providers do not stay disabled behind a restrictive plugin allowlist. Thanks @vincentkoc.
|
||||
- Memory-core/dreaming: retry managed dreaming cron registration after startup when the cron service is not reachable yet, so the scheduled Memory Dreaming Promotion sweep recovers without waiting for heartbeat traffic. Fixes #72841. Thanks @amknight.
|
||||
- Acpx/runtime: validate the runtime session mode at the `AcpxRuntime.ensureSession` wrapper boundary so callers that pass anything other than `persistent` or `oneshot` get a clear `ACP_INVALID_RUNTIME_OPTION` error instead of silently round-tripping through the encoded handle as a default `persistent` mode and later throwing `SessionResumeRequiredError`. Investigation context: #73071. (#73548) Thanks @amknight.
|
||||
|
||||
## 2026.4.27
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import type { AcpRuntime } from "../runtime-api.js";
|
||||
import { AcpRuntimeError, type AcpRuntime } from "../runtime-api.js";
|
||||
import { AcpxRuntime, __testing } from "./runtime.js";
|
||||
|
||||
type TestSessionStore = {
|
||||
@@ -85,6 +85,43 @@ describe("AcpxRuntime fresh reset wrapper", () => {
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
it("rejects unsupported runtime session modes with a clear AcpRuntimeError (issue #73071)", async () => {
|
||||
const baseStore: TestSessionStore = {
|
||||
load: vi.fn(async () => undefined),
|
||||
save: vi.fn(async () => {}),
|
||||
};
|
||||
const { runtime, delegate } = makeRuntime(baseStore);
|
||||
const ensureSpy = vi.spyOn(delegate, "ensureSession").mockResolvedValue({
|
||||
sessionKey: "agent:claude:acp:test",
|
||||
backend: "acpx",
|
||||
runtimeSessionName: "claude",
|
||||
});
|
||||
|
||||
for (const badMode of ["run", "session", "", undefined, null, 0]) {
|
||||
await expect(
|
||||
runtime.ensureSession({
|
||||
sessionKey: "agent:claude:acp:test",
|
||||
agent: "claude",
|
||||
mode: badMode as never,
|
||||
}),
|
||||
).rejects.toMatchObject({
|
||||
name: "AcpRuntimeError",
|
||||
code: "ACP_INVALID_RUNTIME_OPTION",
|
||||
message: expect.stringContaining("Unsupported ACP runtime session mode"),
|
||||
});
|
||||
}
|
||||
|
||||
expect(ensureSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("exposes assertSupportedRuntimeSessionMode as a typed guard", () => {
|
||||
expect(() => __testing.assertSupportedRuntimeSessionMode("persistent")).not.toThrow();
|
||||
expect(() => __testing.assertSupportedRuntimeSessionMode("oneshot")).not.toThrow();
|
||||
expect(() => __testing.assertSupportedRuntimeSessionMode("run" as never)).toThrow(
|
||||
AcpRuntimeError,
|
||||
);
|
||||
});
|
||||
|
||||
it("normalizes OpenClaw Codex model ids for ACP startup", async () => {
|
||||
const baseStore: TestSessionStore = {
|
||||
load: vi.fn(async () => undefined),
|
||||
|
||||
@@ -231,6 +231,25 @@ function failUnsupportedCodexAcpModel(rawModel: string, detail?: string): never
|
||||
);
|
||||
}
|
||||
|
||||
// acpx's `decodeAcpxRuntimeHandleState` only accepts `persistent` and `oneshot`; any other
|
||||
// value silently round-trips through the encoded handle as `persistent` and later throws
|
||||
// `SessionResumeRequiredError` on agent restart. Fail fast at this boundary instead.
|
||||
// See openclaw/openclaw#73071.
|
||||
const SUPPORTED_RUNTIME_SESSION_MODES = new Set(["persistent", "oneshot"] as const);
|
||||
|
||||
function assertSupportedRuntimeSessionMode(
|
||||
mode: unknown,
|
||||
): asserts mode is "persistent" | "oneshot" {
|
||||
if (typeof mode === "string" && SUPPORTED_RUNTIME_SESSION_MODES.has(mode as never)) {
|
||||
return;
|
||||
}
|
||||
const supported = Array.from(SUPPORTED_RUNTIME_SESSION_MODES).join(", ");
|
||||
throw new AcpRuntimeError(
|
||||
"ACP_INVALID_RUNTIME_OPTION",
|
||||
`Unsupported ACP runtime session mode ${JSON.stringify(mode)}. Expected one of: ${supported}.`,
|
||||
);
|
||||
}
|
||||
|
||||
function failUnsupportedCodexAcpThinking(rawThinking: string): never {
|
||||
throw new AcpRuntimeError(
|
||||
"ACP_INVALID_RUNTIME_OPTION",
|
||||
@@ -460,6 +479,7 @@ export class AcpxRuntime implements AcpRuntime {
|
||||
async ensureSession(
|
||||
input: Parameters<AcpRuntime["ensureSession"]>[0],
|
||||
): Promise<AcpRuntimeHandle> {
|
||||
assertSupportedRuntimeSessionMode(input.mode);
|
||||
const command = resolveAgentCommandForName({
|
||||
agentName: input.agent,
|
||||
agentRegistry: this.agentRegistry,
|
||||
@@ -584,6 +604,7 @@ export {
|
||||
|
||||
export const __testing = {
|
||||
appendCodexAcpConfigOverrides,
|
||||
assertSupportedRuntimeSessionMode,
|
||||
codexAcpSessionModelId,
|
||||
isCodexAcpCommand,
|
||||
normalizeCodexAcpModelOverride,
|
||||
|
||||
Reference in New Issue
Block a user