Skip to content

Commit 4e07295

Browse files
committed
refactor(threads): simplify title seed matching
1 parent 83105bd commit 4e07295

2 files changed

Lines changed: 15 additions & 39 deletions

File tree

apps/server/src/orchestration/Layers/ProviderCommandReactor.test.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -323,14 +323,15 @@ describe("ProviderCommandReactor", () => {
323323
it("generates a thread title on the first turn", async () => {
324324
const harness = await createHarness();
325325
const now = new Date().toISOString();
326+
const seededTitle = "Please investigate reconnect failures after restar...";
326327
harness.generateThreadTitle.mockReturnValue(Effect.succeed({ title: "Generated title" }));
327328

328329
await Effect.runPromise(
329330
harness.engine.dispatch({
330331
type: "thread.meta.update",
331332
commandId: CommandId.makeUnsafe("cmd-thread-title-seed"),
332333
threadId: ThreadId.makeUnsafe("thread-1"),
333-
title: "Please investigate reconnect failures after restar...",
334+
title: seededTitle,
334335
}),
335336
);
336337

@@ -345,6 +346,7 @@ describe("ProviderCommandReactor", () => {
345346
text: "Please investigate reconnect failures after restarting the session.",
346347
attachments: [],
347348
},
349+
titleSeed: seededTitle,
348350
interactionMode: DEFAULT_PROVIDER_INTERACTION_MODE,
349351
runtimeMode: "approval-required",
350352
createdAt: now,
@@ -371,6 +373,7 @@ describe("ProviderCommandReactor", () => {
371373
it("does not overwrite an existing custom thread title on the first turn", async () => {
372374
const harness = await createHarness();
373375
const now = new Date().toISOString();
376+
const seededTitle = "Please investigate reconnect failures after restar...";
374377

375378
await Effect.runPromise(
376379
harness.engine.dispatch({
@@ -392,6 +395,7 @@ describe("ProviderCommandReactor", () => {
392395
text: "Please investigate reconnect failures after restarting the session.",
393396
attachments: [],
394397
},
398+
titleSeed: seededTitle,
395399
interactionMode: DEFAULT_PROVIDER_INTERACTION_MODE,
396400
runtimeMode: "approval-required",
397401
createdAt: now,

apps/server/src/orchestration/Layers/ProviderCommandReactor.ts

Lines changed: 10 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import {
1313
} from "@t3tools/contracts";
1414
import { Cache, Cause, Duration, Effect, Equal, Layer, Option, Schema, Stream } from "effect";
1515
import { makeDrainableWorker } from "@t3tools/shared/DrainableWorker";
16-
import { truncate } from "@t3tools/shared/String";
1716

1817
import { resolveThreadWorkspaceCwd } from "../../checkpointing/Utils.ts";
1918
import { GitCore } from "../../git/Services/GitCore.ts";
@@ -76,43 +75,16 @@ const WORKTREE_BRANCH_PREFIX = "t3code";
7675
const TEMP_WORKTREE_BRANCH_PATTERN = new RegExp(`^${WORKTREE_BRANCH_PREFIX}\\/[0-9a-f]{8}$`);
7776
const DEFAULT_THREAD_TITLE = "New thread";
7877

79-
function buildReplaceableThreadTitles(input: {
80-
readonly messageText: string;
81-
readonly attachments?: ReadonlyArray<ChatAttachment>;
82-
readonly titleSeed?: string;
83-
}): ReadonlySet<string> {
84-
const titles = new Set<string>([DEFAULT_THREAD_TITLE]);
85-
const trimmedTitleSeed = input.titleSeed?.trim();
86-
87-
if (trimmedTitleSeed) {
88-
titles.add(trimmedTitleSeed);
89-
return titles;
78+
function canReplaceThreadTitle(currentTitle: string, titleSeed?: string): boolean {
79+
const trimmedCurrentTitle = currentTitle.trim();
80+
if (trimmedCurrentTitle === DEFAULT_THREAD_TITLE) {
81+
return true;
9082
}
9183

92-
const trimmedMessage = input.messageText.trim();
93-
94-
if (trimmedMessage.length > 0) {
95-
titles.add(truncate(trimmedMessage));
96-
return titles;
97-
}
98-
99-
const firstImageAttachment = input.attachments?.find((attachment) => attachment.type === "image");
100-
if (firstImageAttachment) {
101-
titles.add(truncate(`Image: ${firstImageAttachment.name}`));
102-
}
103-
104-
return titles;
105-
}
106-
107-
function isReplaceableThreadTitle(
108-
currentTitle: string,
109-
input: {
110-
readonly messageText: string;
111-
readonly attachments?: ReadonlyArray<ChatAttachment>;
112-
readonly titleSeed?: string;
113-
},
114-
): boolean {
115-
return buildReplaceableThreadTitles(input).has(currentTitle.trim());
84+
const trimmedTitleSeed = titleSeed?.trim();
85+
return trimmedTitleSeed !== undefined && trimmedTitleSeed.length > 0
86+
? trimmedCurrentTitle === trimmedTitleSeed
87+
: false;
11688
}
11789

11890
function isUnknownPendingApprovalRequestError(cause: Cause.Cause<ProviderServiceError>): boolean {
@@ -511,7 +483,7 @@ const make = Effect.gen(function* () {
511483

512484
const thread = yield* resolveThread(input.threadId);
513485
if (!thread) return;
514-
if (!isReplaceableThreadTitle(thread.title, input)) {
486+
if (!canReplaceThreadTitle(thread.title, input.titleSeed)) {
515487
return;
516488
}
517489

@@ -579,7 +551,7 @@ const make = Effect.gen(function* () {
579551
...generationInput,
580552
}).pipe(Effect.forkScoped);
581553

582-
if (isReplaceableThreadTitle(thread.title, generationInput)) {
554+
if (canReplaceThreadTitle(thread.title, event.payload.titleSeed)) {
583555
yield* maybeGenerateThreadTitleForFirstTurn({
584556
threadId: event.payload.threadId,
585557
cwd: generationCwd,

0 commit comments

Comments
 (0)