mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 07:50:43 +00:00
fix: bound configured acp binding readiness
This commit is contained in:
@@ -75,6 +75,9 @@ Docs: https://docs.openclaw.ai
|
||||
- ACP: wait for the configured runtime backend to become healthy before startup
|
||||
identity reconciliation, avoiding transient acpx warnings during Gateway boot.
|
||||
Fixes #40566.
|
||||
- Channels/ACP bindings: time out configured binding readiness checks instead of
|
||||
letting Discord preflight hang forever when an ACP target never settles. Fixes
|
||||
#68776.
|
||||
- Control UI: hide the chat loading skeleton during background history reloads
|
||||
when existing messages or active stream content are already visible, avoiding
|
||||
reload flashes on high-latency local gateways. Fixes #71844. Thanks
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import {
|
||||
__testing,
|
||||
registerSessionBindingAdapter,
|
||||
@@ -6,7 +6,14 @@ import {
|
||||
type SessionBindingRecord,
|
||||
} from "../../infra/outbound/session-binding-service.js";
|
||||
import type { ResolvedAgentRoute } from "../../routing/resolve-route.js";
|
||||
import { resolveRuntimeConversationBindingRoute } from "./binding-routing.js";
|
||||
import {
|
||||
ensureConfiguredBindingRouteReady,
|
||||
resolveRuntimeConversationBindingRoute,
|
||||
} from "./binding-routing.js";
|
||||
import {
|
||||
registerStatefulBindingTargetDriver,
|
||||
unregisterStatefulBindingTargetDriver,
|
||||
} from "./stateful-target-drivers.js";
|
||||
|
||||
function createRoute(): ResolvedAgentRoute {
|
||||
return {
|
||||
@@ -112,3 +119,35 @@ describe("runtime conversation binding route", () => {
|
||||
expect(result.route).toBe(route);
|
||||
});
|
||||
});
|
||||
|
||||
describe("ensureConfiguredBindingRouteReady", () => {
|
||||
afterEach(() => {
|
||||
vi.useRealTimers();
|
||||
unregisterStatefulBindingTargetDriver("slow");
|
||||
});
|
||||
|
||||
it("returns a bounded failure when target readiness never settles", async () => {
|
||||
vi.useFakeTimers();
|
||||
registerStatefulBindingTargetDriver({
|
||||
id: "slow",
|
||||
ensureReady: async () => await new Promise<never>(() => {}),
|
||||
ensureSession: async () => ({
|
||||
ok: false,
|
||||
sessionKey: "agent:slow:binding",
|
||||
error: "not used",
|
||||
}),
|
||||
});
|
||||
|
||||
const resultPromise = ensureConfiguredBindingRouteReady({
|
||||
cfg: {} as never,
|
||||
bindingResolution: { statefulTarget: { driverId: "slow" } } as never,
|
||||
});
|
||||
|
||||
await vi.advanceTimersByTimeAsync(30_000);
|
||||
|
||||
await expect(resultPromise).resolves.toEqual({
|
||||
ok: false,
|
||||
error: "Configured binding route ready check timed out",
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { OpenClawConfig } from "../../config/types.openclaw.js";
|
||||
import { logVerbose } from "../../globals.js";
|
||||
import {
|
||||
getSessionBindingService,
|
||||
type ConversationRef,
|
||||
@@ -11,6 +12,8 @@ import { resolveConfiguredBinding } from "./binding-registry.js";
|
||||
import { ensureConfiguredBindingTargetReady } from "./binding-targets.js";
|
||||
import type { ConfiguredBindingResolution } from "./binding-types.js";
|
||||
|
||||
const CONFIGURED_BINDING_ROUTE_READY_TIMEOUT_MS = 30_000;
|
||||
|
||||
export type ConfiguredBindingRouteResult = {
|
||||
bindingResolution: ConfiguredBindingResolution | null;
|
||||
route: ResolvedAgentRoute;
|
||||
@@ -152,5 +155,34 @@ export async function ensureConfiguredBindingRouteReady(params: {
|
||||
cfg: OpenClawConfig;
|
||||
bindingResolution: ConfiguredBindingResolution | null;
|
||||
}): Promise<{ ok: true } | { ok: false; error: string }> {
|
||||
return await ensureConfiguredBindingTargetReady(params);
|
||||
const readyPromise = ensureConfiguredBindingTargetReady(params);
|
||||
let timer: ReturnType<typeof setTimeout> | undefined;
|
||||
const timeoutToken = Symbol("configured-binding-route-ready-timeout");
|
||||
const timeoutPromise = new Promise<typeof timeoutToken>((resolve) => {
|
||||
timer = setTimeout(() => resolve(timeoutToken), CONFIGURED_BINDING_ROUTE_READY_TIMEOUT_MS);
|
||||
timer.unref?.();
|
||||
});
|
||||
|
||||
try {
|
||||
const result = await Promise.race([readyPromise, timeoutPromise]);
|
||||
if (result !== timeoutToken) {
|
||||
return result;
|
||||
}
|
||||
logVerbose(
|
||||
`configured binding route ready check timed out after ${
|
||||
CONFIGURED_BINDING_ROUTE_READY_TIMEOUT_MS / 1_000
|
||||
}s`,
|
||||
);
|
||||
readyPromise.then(
|
||||
(lateResult) =>
|
||||
logVerbose(
|
||||
`configured binding route ready check settled after timeout (ok=${lateResult.ok})`,
|
||||
),
|
||||
(err) =>
|
||||
logVerbose(`configured binding route ready check rejected after timeout: ${String(err)}`),
|
||||
);
|
||||
return { ok: false, error: "Configured binding route ready check timed out" };
|
||||
} finally {
|
||||
clearTimeout(timer);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user