Skip to content

Commit e5fe9bd

Browse files
authored
fix: reread config on in-process gateway restart (#80161)
* fix: reread config on in-process gateway restart * fix: refresh swift protocol model
1 parent c240b30 commit e5fe9bd

4 files changed

Lines changed: 53 additions & 6 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ Docs: https://docs.openclaw.ai
2020

2121
### Fixes
2222

23+
- Gateway: reread config from disk after the first in-process restart loop startup, preventing SIGUSR1 restarts from reusing a stale startup snapshot and dropping config written after boot. Fixes #79947. Thanks @TheLevti.
2324
- Media/host-read: allow buffer-verified gzip, tar, and 7z archives in the shared host-local media validator alongside ZIP and document attachments.
2425
- Plugins/doctor: invalidate persisted plugin registry snapshots when plugin diagnostics point at deleted source paths, so `openclaw doctor` stops repeating stale warnings after a local extension is replaced by a managed npm plugin. Fixes #80087. (#80134) Thanks @hclsys.
2526
- Cron: let isolated self-cleanup runs inspect their own job run history while keeping other cron jobs and mutation actions blocked. Fixes #80019. Thanks @hclsys.

apps/shared/OpenClawKit/Sources/OpenClawProtocol/GatewayModels.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -716,6 +716,7 @@ public struct AgentParams: Codable, Sendable {
716716
public let bootstrapcontextmode: AnyCodable?
717717
public let bootstrapcontextrunkind: AnyCodable?
718718
public let acpturnsource: String?
719+
public let internalruntimehandoffid: String?
719720
public let internalevents: [[String: AnyCodable]]?
720721
public let inputprovenance: [String: AnyCodable]?
721722
public let voicewaketrigger: String?
@@ -752,6 +753,7 @@ public struct AgentParams: Codable, Sendable {
752753
bootstrapcontextmode: AnyCodable?,
753754
bootstrapcontextrunkind: AnyCodable?,
754755
acpturnsource: String?,
756+
internalruntimehandoffid: String?,
755757
internalevents: [[String: AnyCodable]]?,
756758
inputprovenance: [String: AnyCodable]?,
757759
voicewaketrigger: String?,
@@ -787,6 +789,7 @@ public struct AgentParams: Codable, Sendable {
787789
self.bootstrapcontextmode = bootstrapcontextmode
788790
self.bootstrapcontextrunkind = bootstrapcontextrunkind
789791
self.acpturnsource = acpturnsource
792+
self.internalruntimehandoffid = internalruntimehandoffid
790793
self.internalevents = internalevents
791794
self.inputprovenance = inputprovenance
792795
self.voicewaketrigger = voicewaketrigger
@@ -824,6 +827,7 @@ public struct AgentParams: Codable, Sendable {
824827
case bootstrapcontextmode = "bootstrapContextMode"
825828
case bootstrapcontextrunkind = "bootstrapContextRunKind"
826829
case acpturnsource = "acpTurnSource"
830+
case internalruntimehandoffid = "internalRuntimeHandoffId"
827831
case internalevents = "internalEvents"
828832
case inputprovenance = "inputProvenance"
829833
case voicewaketrigger = "voiceWakeTrigger"

src/cli/gateway-cli/run.option-collisions.test.ts

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ const forceFreePortAndWait = vi.fn(async (_port: number, _opts: unknown) => ({
1919
}));
2020
const waitForPortBindable = vi.fn(async (_port: number, _opts?: unknown) => 0);
2121
const ensureDevGatewayConfig = vi.fn(async (_opts?: unknown) => {});
22-
const runGatewayLoop = vi.fn(async ({ start }: { start: () => Promise<unknown> }) => {
22+
type GatewayLoopStart = (params?: { startupStartedAt?: number }) => Promise<unknown>;
23+
const runGatewayLoop = vi.fn(async ({ start }: { start: GatewayLoopStart }) => {
2324
await start();
2425
});
2526
const gatewayLogMessages = vi.hoisted(() => [] as string[]);
@@ -157,7 +158,7 @@ vi.mock("./dev.js", () => ({
157158
}));
158159

159160
vi.mock("./run-loop.js", () => ({
160-
runGatewayLoop: (params: { start: () => Promise<unknown> }) => runGatewayLoop(params),
161+
runGatewayLoop: (params: { start: GatewayLoopStart }) => runGatewayLoop(params),
161162
}));
162163

163164
describe("gateway run option collisions", () => {
@@ -305,6 +306,41 @@ describe("gateway run option collisions", () => {
305306
);
306307
});
307308

309+
it("uses the startup snapshot only for the first in-process gateway start", async () => {
310+
runGatewayLoop.mockImplementationOnce(async ({ start }: { start: GatewayLoopStart }) => {
311+
await start({ startupStartedAt: 1000 });
312+
await start({ startupStartedAt: 2000 });
313+
});
314+
315+
await runGatewayCli(["gateway", "run", "--allow-unconfigured"]);
316+
317+
expect(startGatewayServer).toHaveBeenCalledTimes(2);
318+
expect(startGatewayServer).toHaveBeenNthCalledWith(
319+
1,
320+
18789,
321+
expect.objectContaining({
322+
startupStartedAt: 1000,
323+
startupConfigSnapshotRead: {
324+
snapshot: configState.snapshot,
325+
},
326+
}),
327+
);
328+
expect(startGatewayServer).toHaveBeenNthCalledWith(
329+
2,
330+
18789,
331+
expect.not.objectContaining({
332+
startupConfigSnapshotRead: expect.anything(),
333+
}),
334+
);
335+
expect(startGatewayServer).toHaveBeenNthCalledWith(
336+
2,
337+
18789,
338+
expect.objectContaining({
339+
startupStartedAt: 2000,
340+
}),
341+
);
342+
});
343+
308344
it("logs when first startup will build missing Control UI assets", async () => {
309345
controlUiState.root = null;
310346

src/cli/gateway-cli/run.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -787,19 +787,25 @@ async function runGatewayCommand(opts: GatewayRunOpts) {
787787
gatewayLog.info("starting...");
788788
startupTrace.mark("cli.gateway-loop");
789789
const healthHost = await resolveGatewayBindHost(bind, cfg.gateway?.customBindHost);
790+
let startupConfigSnapshotReadForNextStart = startupConfigSnapshotRead;
790791
const startLoop = async () =>
791792
await runGatewayLoop({
792793
runtime: defaultRuntime,
793794
lockPort: port,
794795
healthHost,
795-
start: async ({ startupStartedAt } = {}) =>
796-
await startGatewayServer(port, {
796+
start: async ({ startupStartedAt } = {}) => {
797+
const startupConfigSnapshotReadForThisStart = startupConfigSnapshotReadForNextStart;
798+
startupConfigSnapshotReadForNextStart = undefined;
799+
return await startGatewayServer(port, {
797800
bind,
798801
auth: authOverride,
799802
tailscale: tailscaleOverride,
800803
startupStartedAt,
801-
...(startupConfigSnapshotRead ? { startupConfigSnapshotRead } : {}),
802-
}),
804+
...(startupConfigSnapshotReadForThisStart
805+
? { startupConfigSnapshotRead: startupConfigSnapshotReadForThisStart }
806+
: {}),
807+
});
808+
},
803809
});
804810

805811
const { detectRespawnSupervisor } = await import("../../infra/supervisor-markers.js");

0 commit comments

Comments
 (0)