fix(cli): change_title updates metadata.name for visible renames (#1896)

* fix(cli): change_title updates metadata.name for visible renames

Agent change_title was writing metadata.summary only, so spawn --name
sessions kept showing the old title. Route explicit renames through
metadata.name (web rename semantics) across MCP, Codex, and Pi.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(web): voice context prefers metadata.name for session title

Voice bootstrap/full formatters only read summary.text, so after
change_title sets name the spoken context kept a stale title. Use the
same name→summary precedence as getSessionTitle.

Co-authored-by: Cursor <cursoragent@cursor.com>

* chore: polish title rename tests after cold review

Rename the Codex root title test to match last-writer-wins behavior,
and skip the voice full-context summary header when no title signal
exists (avoid literal undefined).

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
HeavyGee
2026-09-21 13:49:53 +01:00
committed by GitHub
co-authored by Cursor
parent b6a27bdc68
commit a259ad3144
17 changed files with 432 additions and 54 deletions
+15 -6
View File
@@ -1144,6 +1144,7 @@ function createSessionStub(
const sessionEvents: Array<{ type: string; [key: string]: unknown }> = [];
const codexMessages: unknown[] = [];
const summaryMessages: unknown[] = [];
const metadataUpdates: Array<Record<string, unknown>> = [];
const thinkingChanges: boolean[] = [];
const foundSessionIds: string[] = [];
const resetThreadCalls: string[] = [];
@@ -1156,6 +1157,11 @@ function createSessionStub(
requests: {},
completedRequests: {}
};
let metadata: Record<string, unknown> = {
path: '/tmp/hapi-update',
host: 'localhost',
name: 'issue-triage-#54'
};
const rpcHandlers = new Map<string, (params: unknown) => unknown>();
const client = {
@@ -1164,7 +1170,10 @@ function createSessionStub(
rpcHandlers.set(method, handler);
}
},
updateMetadata(_handler: (metadata: Record<string, unknown>) => Record<string, unknown>) {},
updateMetadata(handler: (current: Record<string, unknown>) => Record<string, unknown>) {
metadata = handler(metadata);
metadataUpdates.push({ ...metadata });
},
updateAgentState(handler: (state: FakeAgentState) => FakeAgentState) {
agentState = handler(agentState);
},
@@ -1239,6 +1248,8 @@ function createSessionStub(
sessionEvents,
codexMessages,
summaryMessages,
metadataUpdates,
getMetadata: () => metadata,
thinkingChanges,
foundSessionIds,
resetThreadCalls,
@@ -3309,15 +3320,13 @@ describe('codexRemoteLauncher', () => {
it('applies parent-thread hapi change_title after disabling MCP-side title writes', async () => {
harness.emitParentTitleChange = true;
const { session, codexMessages, summaryMessages } = createSessionStub();
const { session, codexMessages, summaryMessages, getMetadata } = createSessionStub();
await codexRemoteLauncher(session as never);
expect(harness.bridgeOptions).toEqual([{ emitTitleSummary: false }]);
expect(summaryMessages).toContainEqual(expect.objectContaining({
type: 'summary',
summary: 'Parent Title'
}));
expect(summaryMessages).toEqual([]);
expect(getMetadata().name).toBe('Parent Title');
expect(codexMessages).toContainEqual(expect.objectContaining({
type: 'tool-call',
name: 'mcp__hapi__change_title',