diff --git a/src/bundled/goose/runtime.test.ts b/src/bundled/goose/runtime.test.ts index 4d1cdc0839..ac9fcea1c9 100644 --- a/src/bundled/goose/runtime.test.ts +++ b/src/bundled/goose/runtime.test.ts @@ -240,6 +240,28 @@ describe("GooseRuntime", () => { ]); }); + it("posts nothing goose wrote without `send`", async () => { + // Text the agent writes outside `send` is narration, not a reply. + for (const reply of ["I've replied to Will.", "Plain answer"]) { + const { deliver, published, runtime } = setup({ reply }); + let activity: Promise = Promise.resolve(); + await deliver(message("1", "@Gander hi"), { + workingUntil: (done: Promise) => { + activity = done; + }, + }); + expect(await activity, reply).toMatchObject({ ok: true, text: reply }); + // The turn's report step ends by notifying, after any post it makes. + await new Promise((resolve) => { + const stop = runtime.subscribe(() => { + stop(); + resolve(); + }); + }); + expect(published, reply).toEqual([]); + } + }); + it("tells a subscriber when a new conversation's first turn starts working", async () => { const { deliver, goose, runtime } = setup({ hold: true }); // As a view does: each notification reads the snapshot again. @@ -268,7 +290,6 @@ describe("GooseRuntime", () => { const { deliver, goose, published } = setup({ scope: "channel", hold: true, - reply: "Forgot to send", }); await deliver(reply("3", one)); await flush(100); @@ -277,17 +298,14 @@ describe("GooseRuntime", () => { const process = goose(); expect(process?.turns).toHaveLength(1); expect(process?.turns[0]?.steered).toHaveLength(1); - process?.turns[0]?.finish(); + process?.exit(1, "crashed\n"); await flush(100); - expect(published).toEqual([ - { - kind: 9, - content: "Forgot to send", - tags: [ - ["h", channel], - ["e", two.id, "", "reply"], - ], - }, + expect(published.map((event) => event.tags)).toEqual([ + [ + ["h", channel], + ["e", two.id, "", "reply"], + ["p", alice], + ], ]); }); @@ -304,24 +322,6 @@ describe("GooseRuntime", () => { expect(memoryOpen).not.toHaveBeenCalled(); }); - it("posts goose's own text in the thread, or at a Solo chat's top level", async () => { - const plain = setup({ reply: "Plain answer" }); - await plain.deliver(message("1", "@Gander hi")); - await flush(100); - expect(plain.published.map((event) => event.tags)).toEqual([ - [ - ["h", channel], - ["e", "1".repeat(64), "", "reply"], - ], - ]); - const solo = setup({ reply: "Plain answer", solo: new Set([channel]) }); - await solo.deliver(message("1", "@Gander hi")); - await flush(100); - expect(solo.published.map((event) => event.tags)).toEqual([ - [["h", channel]], - ]); - }); - it("names the agent in a timer's prompt, so its session is listed as the agent's", async () => { const { runOnce, goose } = setup(); await expect( diff --git a/src/bundled/goose/runtime.ts b/src/bundled/goose/runtime.ts index 0d2f12eba9..a6cffc9a0d 100644 --- a/src/bundled/goose/runtime.ts +++ b/src/bundled/goose/runtime.ts @@ -316,13 +316,10 @@ export class GooseRuntime { this.notify(); void done .then((result) => { - // Of the messages one turn answered, only one has its error or text. + // Of the messages one turn answered, only one has its error. An + // answer goose wrote without `send` stays in local activity. if (result.error !== undefined) return this.report(agent, channel, event, result.error, topLevel); - // An answer goose wrote without `send` is posted for it, so the - // thread is not left waiting. - if (result.text) - return this.post(agent, channel, event, result.text, topLevel); }) .catch((error) => console.warn("goose turn failed", error)) .finally(() => this.notify());