diff --git a/CHANGELOG.md b/CHANGELOG.md index bf3a55eb3..749dc7faf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ - Docker: allow optional home volume + extra bind mounts in `docker-setup.sh`. (#679) — thanks @gabriel-trigo. ### Fixes +- CLI: avoid success message when daemon restart is skipped. (#685) — thanks @carlulsoe. - macOS: stabilize bridge tunnels, guard invoke senders on disconnect, and drain stdout/stderr to avoid deadlocks. (#676) — thanks @ngutman. - Agents/System: clarify sandboxed runtime in system prompt and surface elevated availability when sandboxed. - Auto-reply: prefer `RawBody` for command/directive parsing (WhatsApp + Discord) and prevent fallback runs from clobbering concurrent session updates. (#643) — thanks @mcinteerj. diff --git a/src/auto-reply/reply.ts b/src/auto-reply/reply.ts index b8d8b9a66..6f9be3111 100644 --- a/src/auto-reply/reply.ts +++ b/src/auto-reply/reply.ts @@ -589,7 +589,6 @@ export async function getReplyFromConfig( (agentCfg?.elevatedDefault as ElevatedLevel | undefined) ?? "on") : "off"; - const providerKey = sessionCtx.Provider?.trim().toLowerCase(); const resolvedBlockStreaming = opts?.disableBlockStreaming === true ? "off" diff --git a/src/cli/daemon-cli.ts b/src/cli/daemon-cli.ts index 55a8a1450..793b1ea5b 100644 --- a/src/cli/daemon-cli.ts +++ b/src/cli/daemon-cli.ts @@ -993,7 +993,12 @@ export async function runDaemonStop() { } } -export async function runDaemonRestart() { +/** + * Restart the gateway daemon service. + * @returns `true` if restart succeeded, `false` if the service was not loaded. + * Throws/exits on check or restart failures. + */ +export async function runDaemonRestart(): Promise { const service = resolveGatewayService(); let loaded = false; try { @@ -1001,20 +1006,22 @@ export async function runDaemonRestart() { } catch (err) { defaultRuntime.error(`Gateway service check failed: ${String(err)}`); defaultRuntime.exit(1); - return; + return false; } if (!loaded) { defaultRuntime.log(`Gateway service ${service.notLoadedText}.`); for (const hint of renderGatewayServiceStartHints()) { defaultRuntime.log(`Start with: ${hint}`); } - return; + return false; } try { await service.restart({ stdout: process.stdout }); + return true; } catch (err) { defaultRuntime.error(`Gateway restart failed: ${String(err)}`); defaultRuntime.exit(1); + return false; } } diff --git a/src/cli/update-cli.test.ts b/src/cli/update-cli.test.ts index cf555a6fd..8a5f3296e 100644 --- a/src/cli/update-cli.test.ts +++ b/src/cli/update-cli.test.ts @@ -124,13 +124,40 @@ describe("update-cli", () => { }; vi.mocked(runGatewayUpdate).mockResolvedValue(mockResult); - vi.mocked(runDaemonRestart).mockResolvedValue(); + vi.mocked(runDaemonRestart).mockResolvedValue(true); await updateCommand({ restart: true }); expect(runDaemonRestart).toHaveBeenCalled(); }); + it("updateCommand skips success message when restart does not run", async () => { + const { runGatewayUpdate } = await import("../infra/update-runner.js"); + const { runDaemonRestart } = await import("./daemon-cli.js"); + const { defaultRuntime } = await import("../runtime.js"); + const { updateCommand } = await import("./update-cli.js"); + + const mockResult: UpdateRunResult = { + status: "ok", + mode: "git", + steps: [], + durationMs: 100, + }; + + vi.mocked(runGatewayUpdate).mockResolvedValue(mockResult); + vi.mocked(runDaemonRestart).mockResolvedValue(false); + vi.mocked(defaultRuntime.log).mockClear(); + + await updateCommand({ restart: true }); + + const logLines = vi + .mocked(defaultRuntime.log) + .mock.calls.map((call) => String(call[0])); + expect( + logLines.some((line) => line.includes("Daemon restarted successfully.")), + ).toBe(false); + }); + it("updateCommand validates timeout option", async () => { const { defaultRuntime } = await import("../runtime.js"); const { updateCommand } = await import("./update-cli.js"); diff --git a/src/cli/update-cli.ts b/src/cli/update-cli.ts index 8ae2872f5..8d6f0d4bb 100644 --- a/src/cli/update-cli.ts +++ b/src/cli/update-cli.ts @@ -156,8 +156,8 @@ export async function updateCommand(opts: UpdateCommandOptions): Promise { defaultRuntime.log(theme.heading("Restarting daemon...")); } try { - await runDaemonRestart(); - if (!opts.json) { + const restarted = await runDaemonRestart(); + if (!opts.json && restarted) { defaultRuntime.log(theme.success("Daemon restarted successfully.")); } } catch (err) { diff --git a/src/commands/sandbox-explain.test.ts b/src/commands/sandbox-explain.test.ts index 3fec0f172..d3e5ea2ee 100644 --- a/src/commands/sandbox-explain.test.ts +++ b/src/commands/sandbox-explain.test.ts @@ -42,5 +42,5 @@ describe("sandbox explain command", () => { expect(Array.isArray(parsed.fixIt)).toBe(true); expect(parsed.fixIt).toContain("agents.defaults.sandbox.mode=off"); expect(parsed.fixIt).toContain("tools.sandbox.tools.deny"); - }); + }, 15_000); }); diff --git a/src/discord/monitor.tool-result.test.ts b/src/discord/monitor.tool-result.test.ts index 5963a4d2d..7c36dd37b 100644 --- a/src/discord/monitor.tool-result.test.ts +++ b/src/discord/monitor.tool-result.test.ts @@ -118,7 +118,7 @@ describe("discord tool result dispatch", () => { expect(runtimeError).not.toHaveBeenCalled(); expect(sendMock).toHaveBeenCalledTimes(1); expect(sendMock.mock.calls[0]?.[1]).toMatch(/^PFX /); - }, 10000); + }, 15_000); it("caches channel info lookups between messages", async () => { const { createDiscordMessageHandler } = await import("./monitor.js");