Name sessions opened by handed-off work, and hide empty ones from activity
A session the coordinator's delegation, a routine or a hook opened has no user message and no reply, so it was never named and showed as "Untitled conversation". Tasks now name their session too, and a session with no user message is named from the bot's own messages. Until a session is named, Latest activity shows what it opened with, and sessions with no messages are left out of it. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
@@ -7,6 +7,7 @@ import generateText from "@/src/harness/text/generate-text";
|
||||
import { runWithHarnessUsageScope } from "@/src/harness/usage/harness-usage-scope";
|
||||
import parseSessionName from "./parse-session-name";
|
||||
import sessionNameTranscript from "./session-name-transcript";
|
||||
import sessionReadyToName from "./session-ready-to-name";
|
||||
|
||||
type Params = {
|
||||
session_id: number | string;
|
||||
@@ -16,7 +17,8 @@ type Params = {
|
||||
};
|
||||
|
||||
/**
|
||||
* Names a session once its first exchange is in: the bot writes the title
|
||||
* Names a session once its first exchange is in (or, with no user message,
|
||||
* once the bot has written in it: work handed to it): the bot writes the title
|
||||
* the rail and sessions list show and a short summary of it, from the
|
||||
* session's first messages (however late the first reply came). The summary
|
||||
* says how many messages it covers (`summary_message_count`), so
|
||||
@@ -51,12 +53,7 @@ export default async function nameBotSession({
|
||||
limit: AppData.BotSessionNameMessages,
|
||||
});
|
||||
|
||||
if (
|
||||
!messages.some((message) => message.author == "user") ||
|
||||
!messages.some((message) => message.author == "bot" && !message.carried_from_session_id)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
if (!sessionReadyToName({ messages })) return false;
|
||||
|
||||
const transcript = sessionNameTranscript({ messages });
|
||||
|
||||
|
||||
@@ -235,9 +235,10 @@ export default async function processBotJob({
|
||||
* Once the first exchange is sent, the bot names the session and
|
||||
* previews it. After the reply is pushed, so it never waits on it;
|
||||
* a model that can't answer now leaves the session for the next
|
||||
* reply to name.
|
||||
* job to name. Tasks name too: a session the coordinator's
|
||||
* delegation or a routine opened has no reply to name it.
|
||||
*/
|
||||
if (job.kind == "reply" && !in_room && completion.message?.session_id) {
|
||||
if (!in_room && completion.message?.session_id) {
|
||||
/**
|
||||
* The message's session, not the job's as it was claimed: a
|
||||
* reply that moved the conversation on answered in the new one.
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
import sessionReadyToName from "./session-ready-to-name";
|
||||
|
||||
describe("sessionReadyToName", () => {
|
||||
test("waits for the bot to answer the user", () => {
|
||||
expect(sessionReadyToName({ messages: [{ id: 1, author: "user", text: "Hi" }] })).toBe(false);
|
||||
});
|
||||
|
||||
test("names a first exchange", () => {
|
||||
expect(
|
||||
sessionReadyToName({
|
||||
messages: [
|
||||
{ id: 1, author: "user", text: "Hi" },
|
||||
{ id: 2, author: "bot", text: "Hello" },
|
||||
],
|
||||
}),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
test("names work handed to the bot, with no user message", () => {
|
||||
expect(
|
||||
sessionReadyToName({
|
||||
messages: [{ id: 1, author: "bot", text: "Coordinator asked me to: **SEO lookup**" }],
|
||||
}),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
test("a carried summary alone is not the bot's answer", () => {
|
||||
expect(
|
||||
sessionReadyToName({
|
||||
messages: [
|
||||
{ id: 1, author: "bot", text: "- Crew set up", carried_from_session_id: 9 },
|
||||
{ id: 2, author: "user", text: "Continue here" },
|
||||
],
|
||||
}),
|
||||
).toBe(false);
|
||||
expect(
|
||||
sessionReadyToName({
|
||||
messages: [{ id: 1, author: "bot", text: "- Crew set up", carried_from_session_id: 9 }],
|
||||
}),
|
||||
).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,28 @@
|
||||
import type { SessionOpeningMessage } from "@/src/functions/backend/db/bot-sessions/grab-session-opening";
|
||||
|
||||
type Params = {
|
||||
messages: SessionOpeningMessage[];
|
||||
};
|
||||
|
||||
/**
|
||||
* Whether a session's opening says enough to name it: the bot has said
|
||||
* something of its own (a carried summary doesn't count), and, when the
|
||||
* user wrote, after the user's first message. A session with no user
|
||||
* message at all is work handed to the bot (the coordinator's delegations,
|
||||
* routines, hooks), named from the bot's own messages.
|
||||
*/
|
||||
export default function sessionReadyToName({ messages }: Params) {
|
||||
const has_bot_message = messages.some(
|
||||
(message) => message.author == "bot" && !message.carried_from_session_id,
|
||||
);
|
||||
|
||||
if (!has_bot_message) return false;
|
||||
|
||||
const first_user = messages.findIndex((message) => message.author == "user");
|
||||
|
||||
if (first_user < 0) return true;
|
||||
|
||||
return messages
|
||||
.slice(first_user + 1)
|
||||
.some((message) => message.author == "bot" && !message.carried_from_session_id);
|
||||
}
|
||||
Reference in New Issue
Block a user