Skip to content

Commit 79e1beb

Browse files
MikeRyanDevBenTaylorDev
authored andcommitted
fix(runtime): require identifyUser name in intelligence mode
1 parent febefea commit 79e1beb

7 files changed

Lines changed: 134 additions & 14 deletions

File tree

examples/integrations/langgraph-python-threads/apps/bff/src/server.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ const app = createCopilotEndpoint({
2525
basePath: "/api/copilotkit",
2626
runtime: new CopilotRuntime({
2727
intelligence,
28-
identifyUser: () => ({ id: "jordan-beamson" }),
28+
identifyUser: () => ({ id: "jordan-beamson", name: "Jordan Beamson" }),
2929
licenseToken: process.env.COPILOTKIT_LICENSE_TOKEN,
3030
agents: { default: agent },
3131
openGenerativeUI: true,

packages/runtime/src/v2/runtime/__tests__/handle-connect.test.ts

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,9 @@ describe("handleConnectAgent", () => {
228228
afterRequestMiddleware: undefined,
229229
runner,
230230
mode: "intelligence",
231-
identifyUser: vi.fn().mockResolvedValue({ id: "user-1" }),
231+
identifyUser: vi
232+
.fn()
233+
.mockResolvedValue({ id: "user-1", name: "User One" }),
232234
intelligence: platform,
233235
} as unknown as CopilotRuntime;
234236
};
@@ -384,7 +386,9 @@ describe("handleConnectAgent", () => {
384386
const platform = {
385387
ɵconnectThread: vi.fn().mockResolvedValue(null),
386388
};
387-
const identifyUser = vi.fn().mockResolvedValue({ id: "resolved-user" });
389+
const identifyUser = vi
390+
.fn()
391+
.mockResolvedValue({ id: "resolved-user", name: "Resolved User" });
388392
const runtime = createIntelligenceRuntime(platform);
389393
runtime.identifyUser = identifyUser;
390394
const request = createConnectRequest(
@@ -414,7 +418,28 @@ describe("handleConnectAgent", () => {
414418
ɵconnectThread: vi.fn(),
415419
};
416420
const runtime = createIntelligenceRuntime(platform);
417-
runtime.identifyUser = vi.fn().mockResolvedValue({ id: "" });
421+
runtime.identifyUser = vi
422+
.fn()
423+
.mockResolvedValue({ id: "", name: "User" });
424+
425+
const response = await handleConnectAgent({
426+
runtime,
427+
request: createConnectRequest(),
428+
agentId: "my-agent",
429+
});
430+
431+
expect(response.status).toBe(400);
432+
expect(platform.ɵconnectThread).not.toHaveBeenCalled();
433+
});
434+
435+
it("returns 400 when identifyUser returns an invalid name", async () => {
436+
const platform = {
437+
ɵconnectThread: vi.fn(),
438+
};
439+
const runtime = createIntelligenceRuntime(platform);
440+
runtime.identifyUser = vi
441+
.fn()
442+
.mockResolvedValue({ id: "user-1", name: "" });
418443

419444
const response = await handleConnectAgent({
420445
runtime,

packages/runtime/src/v2/runtime/__tests__/handle-run.test.ts

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,9 @@ describe("handleRunAgent", () => {
298298
generateThreadNames?: boolean;
299299
identifyUser?: (
300300
request: Request,
301-
) => { id: string } | Promise<{ id: string }>;
301+
) =>
302+
| { id: string; name: string }
303+
| Promise<{ id: string; name: string }>;
302304
},
303305
) => {
304306
const runner = Object.create(IntelligenceAgentRunner.prototype);
@@ -318,7 +320,8 @@ describe("handleRunAgent", () => {
318320
generateThreadNames: options?.generateThreadNames ?? false,
319321
intelligence: platform,
320322
identifyUser:
321-
options?.identifyUser ?? vi.fn().mockResolvedValue({ id: "user-1" }),
323+
options?.identifyUser ??
324+
vi.fn().mockResolvedValue({ id: "user-1", name: "User One" }),
322325
} as unknown as CopilotRuntime;
323326
};
324327

@@ -395,7 +398,9 @@ describe("handleRunAgent", () => {
395398
.fn()
396399
.mockResolvedValue({ joinToken: "jt-123", joinCode: "jc-123" }),
397400
};
398-
const identifyUser = vi.fn().mockResolvedValue({ id: "resolved-user" });
401+
const identifyUser = vi
402+
.fn()
403+
.mockResolvedValue({ id: "resolved-user", name: "Resolved User" });
399404
const runtime = createIntelligenceRuntime(agent, platform, {
400405
identifyUser,
401406
});
@@ -870,7 +875,29 @@ describe("handleRunAgent", () => {
870875
ɵacquireThreadLock: vi.fn(),
871876
};
872877
const runtime = createIntelligenceRuntime(agent, platform, {
873-
identifyUser: vi.fn().mockResolvedValue({ id: "" }),
878+
identifyUser: vi.fn().mockResolvedValue({ id: "", name: "User" }),
879+
});
880+
881+
const response = await handleRunAgent({
882+
runtime,
883+
request: createRunRequest(),
884+
agentId: "my-agent",
885+
});
886+
887+
expect(response.status).toBe(400);
888+
expect(platform.getOrCreateThread).not.toHaveBeenCalled();
889+
expect(platform.ɵacquireThreadLock).not.toHaveBeenCalled();
890+
});
891+
892+
it("returns 400 when identifyUser returns an invalid name", async () => {
893+
const agent = createAgentForIntelligence();
894+
const platform = {
895+
getOrCreateThread: vi.fn(),
896+
getThreadMessages: vi.fn(),
897+
ɵacquireThreadLock: vi.fn(),
898+
};
899+
const runtime = createIntelligenceRuntime(agent, platform, {
900+
identifyUser: vi.fn().mockResolvedValue({ id: "user-1", name: "" }),
874901
});
875902

876903
const response = await handleRunAgent({

packages/runtime/src/v2/runtime/__tests__/handle-threads.test.ts

Lines changed: 66 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,13 @@ import {
1010
import { CopilotRuntime } from "../core/runtime";
1111

1212
describe("thread handlers", () => {
13-
const createIdentifyUser = () => vi.fn().mockResolvedValue({ id: "user-1" });
13+
const createIdentifyUser = () =>
14+
vi.fn().mockResolvedValue({ id: "user-1", name: "User One" });
1415

1516
const createIntelligenceRuntime = (options?: {
1617
identifyUser?: (
1718
request: Request,
18-
) => { id: string } | Promise<{ id: string }>;
19+
) => { id: string; name: string } | Promise<{ id: string; name: string }>;
1920
intelligence?: Record<string, unknown>;
2021
}) =>
2122
({
@@ -92,7 +93,25 @@ describe("thread handlers", () => {
9293
};
9394
const runtime = createIntelligenceRuntime({
9495
intelligence,
95-
identifyUser: vi.fn().mockResolvedValue({ id: "" }),
96+
identifyUser: vi.fn().mockResolvedValue({ id: "", name: "User" }),
97+
});
98+
99+
const response = await handleListThreads({
100+
runtime,
101+
request: new Request("https://example.com/threads?agentId=agent-1"),
102+
});
103+
104+
expect(response.status).toBe(400);
105+
expect(intelligence.listThreads).not.toHaveBeenCalled();
106+
});
107+
108+
it("returns 400 when identifyUser returns an invalid name for thread list", async () => {
109+
const intelligence = {
110+
listThreads: vi.fn(),
111+
};
112+
const runtime = createIntelligenceRuntime({
113+
intelligence,
114+
identifyUser: vi.fn().mockResolvedValue({ id: "user-1", name: "" }),
96115
});
97116

98117
const response = await handleListThreads({
@@ -258,7 +277,50 @@ describe("thread handlers", () => {
258277
};
259278
const runtime = createIntelligenceRuntime({
260279
intelligence,
261-
identifyUser: vi.fn().mockResolvedValue({ id: "" }),
280+
identifyUser: vi.fn().mockResolvedValue({ id: "", name: "User" }),
281+
});
282+
283+
const updateResponse = await handleUpdateThread({
284+
runtime,
285+
request: createMutationRequest("/threads/thread-1", "PATCH", {
286+
agentId: "agent-1",
287+
}),
288+
threadId: "thread-1",
289+
});
290+
expect(updateResponse.status).toBe(400);
291+
292+
const archiveResponse = await handleArchiveThread({
293+
runtime,
294+
request: createMutationRequest("/threads/thread-1/archive", "POST", {
295+
agentId: "agent-1",
296+
}),
297+
threadId: "thread-1",
298+
});
299+
expect(archiveResponse.status).toBe(400);
300+
301+
const deleteResponse = await handleDeleteThread({
302+
runtime,
303+
request: createMutationRequest("/threads/thread-1", "DELETE", {
304+
agentId: "agent-1",
305+
}),
306+
threadId: "thread-1",
307+
});
308+
expect(deleteResponse.status).toBe(400);
309+
310+
expect(intelligence.updateThread).not.toHaveBeenCalled();
311+
expect(intelligence.archiveThread).not.toHaveBeenCalled();
312+
expect(intelligence.deleteThread).not.toHaveBeenCalled();
313+
});
314+
315+
it("returns 400 when identifyUser returns an invalid name for thread mutations", async () => {
316+
const intelligence = {
317+
updateThread: vi.fn(),
318+
archiveThread: vi.fn(),
319+
deleteThread: vi.fn(),
320+
};
321+
const runtime = createIntelligenceRuntime({
322+
intelligence,
323+
identifyUser: vi.fn().mockResolvedValue({ id: "user-1", name: "" }),
262324
});
263325

264326
const updateResponse = await handleUpdateThread({

packages/runtime/src/v2/runtime/__tests__/runtime.test.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@ import { IntelligenceAgentRunner } from "../runner/intelligence";
1111

1212
describe("runtime construction", () => {
1313
const agents = {};
14-
const identifyUser = vi.fn().mockResolvedValue({ id: "user-1" });
14+
const identifyUser = vi
15+
.fn()
16+
.mockResolvedValue({ id: "user-1", name: "User One" });
1517
const createMockIntelligence = (): CopilotKitIntelligence =>
1618
({
1719
ɵgetRunnerWsUrl: vi.fn().mockReturnValue("ws://runner.example"),

packages/runtime/src/v2/runtime/core/runtime.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ interface BaseCopilotRuntimeOptions extends CopilotRuntimeMiddlewares {
141141

142142
export interface CopilotRuntimeUser {
143143
id: string;
144+
name: string;
144145
}
145146

146147
export type IdentifyUserCallback = (

packages/runtime/src/v2/runtime/handlers/shared/resolve-intelligence-user.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,11 @@ export async function resolveIntelligenceUser(params: {
1616
if (!isValidIdentifier(user?.id)) {
1717
return errorResponse("identifyUser must return a valid user id", 400);
1818
}
19+
if (typeof user?.name !== "string" || user.name.trim().length === 0) {
20+
return errorResponse("identifyUser must return a valid user name", 400);
21+
}
1922

20-
return { id: user.id };
23+
return { id: user.id, name: user.name };
2124
} catch (error) {
2225
console.error("Error identifying intelligence user:", error);
2326
return errorResponse("Failed to identify user", 500);

0 commit comments

Comments
 (0)