Skip to content

Commit b62dde6

Browse files
committed
TUI: keep /new hook source context
1 parent 37d84d0 commit b62dde6

7 files changed

Lines changed: 45 additions & 5 deletions

File tree

src/gateway/protocol/schema/sessions.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ export const SessionsResetParamsSchema = Type.Object(
9494
{
9595
key: NonEmptyString,
9696
reason: Type.Optional(Type.Union([Type.Literal("new"), Type.Literal("reset")])),
97+
hookSourceKey: Type.Optional(NonEmptyString),
9798
},
9899
{ additionalProperties: false },
99100
);

src/gateway/server-methods/sessions.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,7 @@ export const sessionsHandlers: GatewayRequestHandlers = {
242242
key,
243243
reason,
244244
commandSource: "gateway:sessions.reset",
245+
hookSourceKey: typeof p.hookSourceKey === "string" ? p.hookSourceKey : undefined,
245246
});
246247
if (!result.ok) {
247248
respond(false, undefined, result.error);

src/gateway/server.sessions.gateway-server-sessions-a.test.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1253,6 +1253,35 @@ describe("gateway server sessions", () => {
12531253
ws.close();
12541254
});
12551255

1256+
test("sessions.reset can use a separate hookSourceKey for command:new context", async () => {
1257+
const { dir } = await createSessionStoreDir();
1258+
await writeSingleLineSession(dir, "sess-main", "hello");
1259+
1260+
await writeSessionStore({
1261+
entries: {
1262+
main: { sessionId: "sess-main", updatedAt: Date.now() },
1263+
},
1264+
});
1265+
1266+
const { ws } = await openClient();
1267+
const reset = await rpcReq<{ ok: true; key: string }>(ws, "sessions.reset", {
1268+
key: "agent:main:tui-hook-target",
1269+
reason: "new",
1270+
hookSourceKey: "main",
1271+
});
1272+
expect(reset.ok).toBe(true);
1273+
expect(sessionHookMocks.triggerInternalHook).toHaveBeenCalledTimes(1);
1274+
const event = (
1275+
sessionHookMocks.triggerInternalHook.mock.calls as unknown as Array<[unknown]>
1276+
)[0]?.[0] as { sessionKey?: string; context?: { previousSessionEntry?: unknown } } | undefined;
1277+
if (!event) {
1278+
throw new Error("expected session hook event");
1279+
}
1280+
expect(event.sessionKey).toBe("agent:main:tui-hook-target");
1281+
expect(event.context?.previousSessionEntry).toMatchObject({ sessionId: "sess-main" });
1282+
ws.close();
1283+
});
1284+
12561285
test("sessions.reset returns unavailable when active run does not stop", async () => {
12571286
const { dir, storePath } = await seedActiveMainSession();
12581287
const waitCallCountAtSnapshotClear: number[] = [];

src/gateway/session-reset-service.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,7 @@ export async function performGatewaySessionReset(params: {
254254
key: string;
255255
reason: "new" | "reset";
256256
commandSource: string;
257+
hookSourceKey?: string;
257258
}): Promise<
258259
| { ok: true; key: string; entry: SessionEntry }
259260
| { ok: false; error: ReturnType<typeof errorShape> }
@@ -265,13 +266,19 @@ export async function performGatewaySessionReset(params: {
265266
})();
266267
const { entry, legacyKey, canonicalKey } = loadSessionEntry(params.key);
267268
const hadExistingEntry = Boolean(entry);
269+
const hookSourceEntry =
270+
typeof params.hookSourceKey === "string" &&
271+
params.hookSourceKey.trim() &&
272+
params.hookSourceKey !== params.key
273+
? loadSessionEntry(params.hookSourceKey).entry
274+
: entry;
268275
const hookEvent = createInternalHookEvent(
269276
"command",
270277
params.reason,
271278
target.canonicalKey ?? params.key,
272279
{
273-
sessionEntry: entry,
274-
previousSessionEntry: entry,
280+
sessionEntry: hookSourceEntry,
281+
previousSessionEntry: hookSourceEntry,
275282
commandSource: params.commandSource,
276283
cfg,
277284
},

src/tui/gateway-chat.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -250,10 +250,11 @@ export class GatewayChatClient {
250250
return await this.client.request<SessionsPatchResult>("sessions.patch", opts);
251251
}
252252

253-
async resetSession(key: string, reason?: "new" | "reset") {
253+
async resetSession(key: string, reason?: "new" | "reset", hookSourceKey?: string) {
254254
return await this.client.request("sessions.reset", {
255255
key,
256256
...(reason ? { reason } : {}),
257+
...(hookSourceKey ? { hookSourceKey } : {}),
257258
});
258259
}
259260

src/tui/tui-command-handlers.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ type LoadHistoryMock = ReturnType<typeof vi.fn> & (() => Promise<void>);
55
type SetActivityStatusMock = ReturnType<typeof vi.fn> & ((text: string) => void);
66
type SetSessionMock = ReturnType<typeof vi.fn> & ((key: string) => Promise<void>);
77
type ResetSessionMock = ReturnType<typeof vi.fn> &
8-
((key: string, reason?: "new" | "reset") => Promise<{ ok: true }>);
8+
((key: string, reason?: "new" | "reset", hookSourceKey?: string) => Promise<{ ok: true }>);
99

1010
function createHarness(params?: {
1111
sendChat?: ReturnType<typeof vi.fn>;
@@ -166,6 +166,7 @@ describe("tui command handlers", () => {
166166
/^agent:main:tui-[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/,
167167
),
168168
"new",
169+
"agent:main:main",
169170
);
170171
// /reset still resets the shared session in place.
171172
expect(resetSession).toHaveBeenNthCalledWith(2, "agent:main:main", "reset");

src/tui/tui-command-handlers.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -465,7 +465,7 @@ export function createCommandHandlers(context: CommandHandlerContext) {
465465
// to other connected TUI clients sharing the original session key.
466466
const uniqueKey = `tui-${randomUUID()}`;
467467
const nextSessionKey = `agent:${normalizeAgentId(state.currentAgentId)}:${uniqueKey}`;
468-
await client.resetSession(nextSessionKey, "new");
468+
await client.resetSession(nextSessionKey, "new", state.currentSessionKey);
469469
await setSession(uniqueKey);
470470
chatLog.addSystem(`new session: ${formatSessionKey(nextSessionKey)}`);
471471
} catch (err) {

0 commit comments

Comments
 (0)