fix(sessions): stop duplicating user message for slash commands

A slash command sent as the first (or any idle) message showed the raw
command text twice until reload: once from the client's optimistic insert
and once from the server's message.append echo, neither of which converges
with the agent's canonical expanded message (e.g. a /skill:* block).

Make commands obey the same source-of-truth contract as prompts:
- client no longer inserts the raw command text optimistically; it shows
  the existing per-session sending indicator instead
- forwarded runtime/skill commands return a bare done result rather than a
  synthetic "Accepted ..." line
- server suppresses the raw message.append echo for command-forwarded
  prompts (threaded through the compaction queue too)

Result: pre-reload state matches reload, with no transient duplicate.
This commit is contained in:
Federico Jaramillo Martinez
2026-06-17 00:02:04 +02:00
parent dd23b3e054
commit e77ab98361
6 changed files with 90 additions and 13 deletions
@@ -60,7 +60,9 @@ describe("SessionCommandService", () => {
const service = new SessionCommandService(() => getActive(active), prompt, eventPublisher());
await expect(service.run("s1", "/missing")).resolves.toEqual({ type: "unsupported", message: "Unknown command: /missing" });
await expect(service.run("s1", "/ext arg")).resolves.toEqual({ type: "done", message: "Accepted /ext arg" });
// Forwarded runtime commands return a bare done result: the agent streams
// back the canonical expanded message, so no synthetic "Accepted" line.
await expect(service.run("s1", "/ext arg")).resolves.toEqual({ type: "done" });
await expect(service.run("s1", "/template arg")).resolves.toMatchObject({ type: "done" });
await expect(service.run("s1", "/skill:skill-a arg")).resolves.toMatchObject({ type: "done" });
expect(prompt).toHaveBeenCalledTimes(3);