mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 09:30:43 +00:00
fix(gateway): preserve restart continuation chat type
This commit is contained in:
@@ -18,6 +18,7 @@ Docs: https://docs.openclaw.ai
|
||||
|
||||
### Fixes
|
||||
|
||||
- Gateway/restart: preserve group and channel chat context when resuming an agent turn after a Gateway restart, so continuation replies keep the same prompt, routing, and tool-status behavior as the original conversation.
|
||||
- Gateway/pairing: shared-secret loopback CLI clients now silently auto-approve `metadata-upgrade` pairing (platform / device family refresh) instead of being disconnected with `1008 pairing required`. This matches the scope-upgrade and role-upgrade behavior added in #69431 and unblocks non-interactive CLI automation when a paired-device record has a stale platform string (e.g. device key replicated across hosts, install migrated between OSes, or platform-string format changed between OpenClaw versions). Browser / Control-UI clients keep the existing approval-required flow for metadata changes.
|
||||
- Gateway/pairing: treat any forwarded-header evidence (`Forwarded`, `X-Forwarded-*`, or `X-Real-IP`) as proxied WebSocket traffic before pairing locality checks, so reverse-proxy topologies cannot use the loopback shared-secret helper auto-pairing path.
|
||||
- Gateway/pairing webchat: render `/pair qr` replies as structured media instead of raw markdown text, preserve inline reply threading and silent-control handling on media replies, avoid persisting sensitive QR images into transcript history, and keep local webchat media embedding behind internal-only trust markers. (#70047) Thanks @BunsDev.
|
||||
|
||||
@@ -432,6 +432,51 @@ describe("scheduleRestartSentinelWake", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("preserves the session chat type for agentTurn continuations", async () => {
|
||||
mocks.consumeRestartSentinel.mockResolvedValue({
|
||||
payload: {
|
||||
sessionKey: "agent:main:group",
|
||||
deliveryContext: {
|
||||
channel: "telegram",
|
||||
to: "telegram:-1001",
|
||||
accountId: "default",
|
||||
},
|
||||
ts: 123,
|
||||
continuation: {
|
||||
kind: "agentTurn",
|
||||
message: "continue",
|
||||
},
|
||||
},
|
||||
} as Awaited<ReturnType<typeof mocks.consumeRestartSentinel>>);
|
||||
mocks.loadSessionEntry.mockReturnValue({
|
||||
cfg: {},
|
||||
entry: {
|
||||
sessionId: "agent:main:group",
|
||||
updatedAt: 0,
|
||||
origin: { provider: "telegram", chatType: "group" },
|
||||
},
|
||||
store: {},
|
||||
storePath: "/tmp/sessions.json",
|
||||
canonicalKey: "agent:main:group",
|
||||
legacyKey: undefined,
|
||||
});
|
||||
mocks.resolveOutboundTarget.mockReturnValue({ ok: true as const, to: "telegram:-1001" });
|
||||
|
||||
await scheduleRestartSentinelWake({ deps: {} as never });
|
||||
|
||||
expect(mocks.recordInboundSessionAndDispatchReply).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
channel: "telegram",
|
||||
routeSessionKey: "agent:main:group",
|
||||
ctxPayload: expect.objectContaining({
|
||||
ChatType: "group",
|
||||
OriginatingChannel: "telegram",
|
||||
OriginatingTo: "telegram:-1001",
|
||||
}),
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("preserves derived reply transport ids in continuation context", async () => {
|
||||
mocks.getChannelPlugin.mockReturnValue({
|
||||
id: "whatsapp",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { resolveSessionAgentId } from "../agents/agent-scope.js";
|
||||
import { finalizeInboundContext } from "../auto-reply/reply/inbound-context.js";
|
||||
import { dispatchReplyWithBufferedBlockDispatcher } from "../auto-reply/reply/provider-dispatcher.js";
|
||||
import type { ChatType } from "../channels/chat-type.js";
|
||||
import { getChannelPlugin, normalizeChannelId } from "../channels/plugins/index.js";
|
||||
import { recordInboundSession } from "../channels/session.js";
|
||||
import type { CliDeps } from "../cli/deps.types.js";
|
||||
@@ -149,6 +150,7 @@ type RestartContinuationRoute = {
|
||||
accountId?: string;
|
||||
replyToId?: string;
|
||||
threadId?: string;
|
||||
chatType: ChatType;
|
||||
};
|
||||
|
||||
function resolveRestartContinuationRoute(params: {
|
||||
@@ -157,6 +159,7 @@ function resolveRestartContinuationRoute(params: {
|
||||
accountId?: string;
|
||||
replyToId?: string;
|
||||
threadId?: string;
|
||||
chatType: ChatType;
|
||||
}): RestartContinuationRoute | undefined {
|
||||
if (!params.channel || !params.to) {
|
||||
return undefined;
|
||||
@@ -167,6 +170,7 @@ function resolveRestartContinuationRoute(params: {
|
||||
...(params.accountId ? { accountId: params.accountId } : {}),
|
||||
...(params.replyToId ? { replyToId: params.replyToId } : {}),
|
||||
...(params.threadId ? { threadId: params.threadId } : {}),
|
||||
chatType: params.chatType,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -246,7 +250,7 @@ async function dispatchRestartSentinelContinuation(params: {
|
||||
Timestamp: Date.now(),
|
||||
Provider: route.channel,
|
||||
Surface: route.channel,
|
||||
ChatType: "direct",
|
||||
ChatType: route.chatType,
|
||||
CommandAuthorized: true,
|
||||
ReplyToId: route.replyToId,
|
||||
OriginatingChannel: route.channel,
|
||||
@@ -337,12 +341,14 @@ async function loadRestartSentinelStartupTask(params: {
|
||||
|
||||
const sentinelContext = payload.deliveryContext;
|
||||
let sessionDeliveryContext = deliveryContextFromSession(entry);
|
||||
let chatType = entry?.origin?.chatType ?? "direct";
|
||||
if (
|
||||
!hasRoutableDeliveryContext(sessionDeliveryContext) &&
|
||||
baseSessionKey &&
|
||||
baseSessionKey !== sessionKey
|
||||
) {
|
||||
const { entry: baseEntry } = loadSessionEntry(baseSessionKey);
|
||||
chatType = entry?.origin?.chatType ?? baseEntry?.origin?.chatType ?? "direct";
|
||||
sessionDeliveryContext = mergeDeliveryContext(
|
||||
sessionDeliveryContext,
|
||||
deliveryContextFromSession(baseEntry),
|
||||
@@ -426,6 +432,7 @@ async function loadRestartSentinelStartupTask(params: {
|
||||
accountId: origin?.accountId,
|
||||
replyToId,
|
||||
threadId: resolvedThreadId,
|
||||
chatType,
|
||||
}),
|
||||
});
|
||||
} catch (err) {
|
||||
|
||||
Reference in New Issue
Block a user