Skip to content

Commit 14159d2

Browse files
committed
fix(doctor): make restart follow-up actionable
1 parent 11dfef2 commit 14159d2

5 files changed

Lines changed: 33 additions & 9 deletions

File tree

src/agents/openclaw-gateway-tool.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ describe("gateway tool", () => {
219219
};
220220
expect(parsed.payload?.kind).toBe("restart");
221221
expect(parsed.payload?.doctorHint).toBe(
222-
"Run: openclaw --profile isolated doctor --non-interactive",
222+
"Recommended follow-up: run openclaw --profile isolated doctor --non-interactive in a terminal or approvals-capable OpenClaw surface.",
223223
);
224224
},
225225
);

src/agents/tools/gateway-tool.test.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,10 @@ const {
2222
},
2323
threadId: "thread-42",
2424
})),
25-
formatDoctorNonInteractiveHintMock: vi.fn(() => "Run: openclaw doctor --non-interactive"),
25+
formatDoctorNonInteractiveHintMock: vi.fn(
26+
() =>
27+
"Recommended follow-up: run openclaw doctor --non-interactive in a terminal or approvals-capable OpenClaw surface.",
28+
),
2629
writeRestartSentinelMock: vi.fn(async (_payload: RestartSentinelPayload) => "/tmp/restart"),
2730
removeRestartSentinelFileMock: vi.fn(async (_path: string | null | undefined) => undefined),
2831
scheduleGatewaySigusr1RestartMock: vi.fn((_opts?: ScheduleGatewayRestartArgs) => ({
@@ -98,7 +101,9 @@ describe("gateway tool restart continuation", () => {
98101
threadId: "thread-42",
99102
});
100103
formatDoctorNonInteractiveHintMock.mockReset();
101-
formatDoctorNonInteractiveHintMock.mockReturnValue("Run: openclaw doctor --non-interactive");
104+
formatDoctorNonInteractiveHintMock.mockReturnValue(
105+
"Recommended follow-up: run openclaw doctor --non-interactive in a terminal or approvals-capable OpenClaw surface.",
106+
);
102107
writeRestartSentinelMock.mockReset();
103108
writeRestartSentinelMock.mockResolvedValue("/tmp/restart");
104109
removeRestartSentinelFileMock.mockClear();

src/auto-reply/reply/commands-session-restart.test.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,10 @@ const mocks = vi.hoisted(() => ({
1616
},
1717
threadId: "thread-1",
1818
})),
19-
formatDoctorNonInteractiveHint: vi.fn(() => "Run: openclaw doctor --non-interactive"),
19+
formatDoctorNonInteractiveHint: vi.fn(
20+
() =>
21+
"Recommended follow-up: run openclaw doctor --non-interactive in a terminal or approvals-capable OpenClaw surface.",
22+
),
2023
writeRestartSentinel: vi.fn(async (_payload: RestartSentinelPayload) => "/tmp/sentinel.json"),
2124
scheduleGatewaySigusr1Restart: vi.fn((_opts?: ScheduleGatewayRestartArgs) => ({
2225
scheduled: true,
@@ -148,7 +151,9 @@ describe("handleRestartCommand", () => {
148151
kind: "agentTurn",
149152
message: DEFAULT_RESTART_SUCCESS_CONTINUATION_MESSAGE,
150153
});
151-
expect(sentinelPayload?.doctorHint).toBe("Run: openclaw doctor --non-interactive");
154+
expect(sentinelPayload?.doctorHint).toBe(
155+
"Recommended follow-up: run openclaw doctor --non-interactive in a terminal or approvals-capable OpenClaw surface.",
156+
);
152157
expect(sentinelPayload?.stats).toEqual({
153158
mode: "gateway.restart",
154159
reason: "/restart",

src/infra/restart-sentinel.test.ts

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -367,9 +367,20 @@ describe("restart sentinel message dedup", () => {
367367
expect(result).toContain("Reason: /restart");
368368
});
369369

370-
it("formats the non-interactive doctor command", () => {
371-
expect(formatDoctorNonInteractiveHint({ PATH: "/usr/bin:/bin" })).toContain(
372-
"openclaw doctor --non-interactive",
370+
it("formats the non-interactive doctor command as actionability guidance", () => {
371+
expect(formatDoctorNonInteractiveHint({ PATH: "/usr/bin:/bin" })).toBe(
372+
"Recommended follow-up: run openclaw doctor --non-interactive in a terminal or approvals-capable OpenClaw surface.",
373+
);
374+
});
375+
376+
it("keeps profile-aware doctor guidance actionable outside constrained delivery surfaces", () => {
377+
expect(
378+
formatDoctorNonInteractiveHint({
379+
OPENCLAW_PROFILE: "isolated",
380+
PATH: "/usr/bin:/bin",
381+
}),
382+
).toBe(
383+
"Recommended follow-up: run openclaw --profile isolated doctor --non-interactive in a terminal or approvals-capable OpenClaw surface.",
373384
);
374385
});
375386
});

src/infra/restart-sentinel.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,10 @@ const SENTINEL_FILENAME = "restart-sentinel.json";
7373
export function formatDoctorNonInteractiveHint(
7474
env: Record<string, string | undefined> = process.env as Record<string, string | undefined>,
7575
): string {
76-
return `Run: ${formatCliCommand("openclaw doctor --non-interactive", env)}`;
76+
return `Recommended follow-up: run ${formatCliCommand(
77+
"openclaw doctor --non-interactive",
78+
env,
79+
)} in a terminal or approvals-capable OpenClaw surface.`;
7780
}
7881

7982
export function resolveRestartSentinelPath(env: NodeJS.ProcessEnv = process.env): string {

0 commit comments

Comments
 (0)