@copilotkit/runtime 1.56.3 → 1.56.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/package.cjs +1 -1
- package/dist/package.mjs +1 -1
- package/dist/v2/index.d.cts +2 -2
- package/dist/v2/index.d.mts +2 -2
- package/dist/v2/runtime/core/fetch-handler.cjs +2 -0
- package/dist/v2/runtime/core/fetch-handler.cjs.map +1 -1
- package/dist/v2/runtime/core/fetch-handler.d.cts.map +1 -1
- package/dist/v2/runtime/core/fetch-handler.d.mts.map +1 -1
- package/dist/v2/runtime/core/fetch-handler.mjs +2 -0
- package/dist/v2/runtime/core/fetch-handler.mjs.map +1 -1
- package/dist/v2/runtime/core/runtime.d.mts +0 -1
- package/dist/v2/runtime/core/runtime.d.mts.map +1 -1
- package/dist/v2/runtime/handlers/handle-connect.cjs +2 -3
- package/dist/v2/runtime/handlers/handle-connect.cjs.map +1 -1
- package/dist/v2/runtime/handlers/handle-connect.mjs +2 -3
- package/dist/v2/runtime/handlers/handle-connect.mjs.map +1 -1
- package/dist/v2/runtime/handlers/intelligence/connect.cjs +21 -31
- package/dist/v2/runtime/handlers/intelligence/connect.cjs.map +1 -1
- package/dist/v2/runtime/handlers/intelligence/connect.mjs +22 -31
- package/dist/v2/runtime/handlers/intelligence/connect.mjs.map +1 -1
- package/dist/v2/runtime/handlers/intelligence/run.cjs +111 -26
- package/dist/v2/runtime/handlers/intelligence/run.cjs.map +1 -1
- package/dist/v2/runtime/handlers/intelligence/run.mjs +111 -26
- package/dist/v2/runtime/handlers/intelligence/run.mjs.map +1 -1
- package/dist/v2/runtime/handlers/shared/intelligence-utils.cjs +7 -3
- package/dist/v2/runtime/handlers/shared/intelligence-utils.cjs.map +1 -1
- package/dist/v2/runtime/handlers/shared/intelligence-utils.mjs +7 -3
- package/dist/v2/runtime/handlers/shared/intelligence-utils.mjs.map +1 -1
- package/dist/v2/runtime/index.d.cts +1 -1
- package/dist/v2/runtime/index.d.mts +1 -2
- package/dist/v2/runtime/index.d.mts.map +1 -1
- package/dist/v2/runtime/intelligence-platform/client.cjs +5 -2
- package/dist/v2/runtime/intelligence-platform/client.cjs.map +1 -1
- package/dist/v2/runtime/intelligence-platform/client.d.cts +16 -18
- package/dist/v2/runtime/intelligence-platform/client.d.cts.map +1 -1
- package/dist/v2/runtime/intelligence-platform/client.d.mts +16 -18
- package/dist/v2/runtime/intelligence-platform/client.d.mts.map +1 -1
- package/dist/v2/runtime/intelligence-platform/client.mjs +5 -2
- package/dist/v2/runtime/intelligence-platform/client.mjs.map +1 -1
- package/dist/v2/runtime/runner/agent-runner.cjs.map +1 -1
- package/dist/v2/runtime/runner/agent-runner.d.cts +0 -1
- package/dist/v2/runtime/runner/agent-runner.d.cts.map +1 -1
- package/dist/v2/runtime/runner/agent-runner.d.mts +0 -1
- package/dist/v2/runtime/runner/agent-runner.d.mts.map +1 -1
- package/dist/v2/runtime/runner/agent-runner.mjs.map +1 -1
- package/dist/v2/runtime/runner/index.d.cts +1 -1
- package/dist/v2/runtime/runner/index.d.mts +1 -1
- package/dist/v2/runtime/runner/intelligence.cjs +30 -5
- package/dist/v2/runtime/runner/intelligence.cjs.map +1 -1
- package/dist/v2/runtime/runner/intelligence.d.cts +7 -1
- package/dist/v2/runtime/runner/intelligence.d.cts.map +1 -1
- package/dist/v2/runtime/runner/intelligence.d.mts +7 -1
- package/dist/v2/runtime/runner/intelligence.d.mts.map +1 -1
- package/dist/v2/runtime/runner/intelligence.mjs +30 -5
- package/dist/v2/runtime/runner/intelligence.mjs.map +1 -1
- package/dist/v2/runtime/telemetry/instance-created.cjs +33 -0
- package/dist/v2/runtime/telemetry/instance-created.cjs.map +1 -0
- package/dist/v2/runtime/telemetry/instance-created.mjs +33 -0
- package/dist/v2/runtime/telemetry/instance-created.mjs.map +1 -0
- package/dist/v2/runtime/telemetry/telemetry-client.cjs +1 -38
- package/dist/v2/runtime/telemetry/telemetry-client.cjs.map +1 -1
- package/dist/v2/runtime/telemetry/telemetry-client.mjs +1 -37
- package/dist/v2/runtime/telemetry/telemetry-client.mjs.map +1 -1
- package/package.json +2 -2
- package/src/v2/runtime/__tests__/express-single-telemetry.integration.test.ts +65 -0
- package/src/v2/runtime/__tests__/express-telemetry.integration.test.ts +101 -0
- package/src/v2/runtime/__tests__/handle-connect.test.ts +155 -48
- package/src/v2/runtime/__tests__/handle-run.test.ts +380 -29
- package/src/v2/runtime/__tests__/hono-single-telemetry.integration.test.ts +46 -0
- package/src/v2/runtime/__tests__/hono-telemetry.integration.test.ts +99 -0
- package/src/v2/runtime/__tests__/intelligence-run-telemetry.test.ts +194 -0
- package/src/v2/runtime/__tests__/sse-response-telemetry.test.ts +108 -0
- package/src/v2/runtime/__tests__/telemetry.test.ts +0 -61
- package/src/v2/runtime/core/fetch-handler.ts +3 -0
- package/src/v2/runtime/handlers/handle-connect.ts +1 -2
- package/src/v2/runtime/handlers/intelligence/connect.ts +48 -68
- package/src/v2/runtime/handlers/intelligence/run.ts +162 -21
- package/src/v2/runtime/handlers/shared/intelligence-utils.ts +21 -1
- package/src/v2/runtime/intelligence-platform/__tests__/client.test.ts +33 -39
- package/src/v2/runtime/intelligence-platform/client.ts +36 -31
- package/src/v2/runtime/runner/__tests__/intelligence-runner.test.ts +15 -7
- package/src/v2/runtime/runner/agent-runner.ts +0 -1
- package/src/v2/runtime/runner/intelligence.ts +47 -6
- package/src/v2/runtime/telemetry/__tests__/instance-created.test.ts +96 -0
- package/src/v2/runtime/telemetry/instance-created.ts +44 -0
- package/src/v2/runtime/telemetry/telemetry-client.ts +1 -57
- package/dist/v2/runtime/intelligence-platform/index.d.mts +0 -2
- package/dist/v2/runtime/telemetry/utils.cjs +0 -15
- package/dist/v2/runtime/telemetry/utils.cjs.map +0 -1
- package/dist/v2/runtime/telemetry/utils.mjs +0 -14
- package/dist/v2/runtime/telemetry/utils.mjs.map +0 -1
- package/src/v2/runtime/telemetry/utils.ts +0 -15
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Observable } from "rxjs";
|
|
2
2
|
import { describe, it, expect, vi } from "vitest";
|
|
3
|
-
import { AbstractAgent, HttpAgent } from "@ag-ui/client";
|
|
3
|
+
import { AbstractAgent, BaseEvent, EventType, HttpAgent } from "@ag-ui/client";
|
|
4
4
|
import { A2UIMiddleware } from "@ag-ui/a2ui-middleware";
|
|
5
5
|
import { handleRunAgent } from "../handlers/handle-run";
|
|
6
6
|
import { CopilotRuntime } from "../core/runtime";
|
|
@@ -285,7 +285,7 @@ describe("handleRunAgent", () => {
|
|
|
285
285
|
expect(useSpy).not.toHaveBeenCalled();
|
|
286
286
|
});
|
|
287
287
|
|
|
288
|
-
describe("IntelligenceAgentRunner
|
|
288
|
+
describe("IntelligenceAgentRunner realtime credentials path", () => {
|
|
289
289
|
/** Loose mock type for CopilotKitIntelligence — avoids `as any` while the class has private fields. */
|
|
290
290
|
interface MockIntelligencePlatform {
|
|
291
291
|
[key: string]: ((...args: any[]) => any) | undefined;
|
|
@@ -296,6 +296,8 @@ describe("handleRunAgent", () => {
|
|
|
296
296
|
platform?: MockIntelligencePlatform,
|
|
297
297
|
options?: {
|
|
298
298
|
generateThreadNames?: boolean;
|
|
299
|
+
lockHeartbeatIntervalSeconds?: number;
|
|
300
|
+
lockTtlSeconds?: number;
|
|
299
301
|
identifyUser?: (
|
|
300
302
|
request: Request,
|
|
301
303
|
) =>
|
|
@@ -318,7 +320,13 @@ describe("handleRunAgent", () => {
|
|
|
318
320
|
runner,
|
|
319
321
|
mode: "intelligence",
|
|
320
322
|
generateThreadNames: options?.generateThreadNames ?? false,
|
|
321
|
-
|
|
323
|
+
lockHeartbeatIntervalSeconds:
|
|
324
|
+
options?.lockHeartbeatIntervalSeconds ?? 15,
|
|
325
|
+
lockTtlSeconds: options?.lockTtlSeconds ?? 20,
|
|
326
|
+
intelligence: {
|
|
327
|
+
ɵgetClientWsUrl: vi.fn(() => "wss://runtime.example/client"),
|
|
328
|
+
...platform,
|
|
329
|
+
},
|
|
322
330
|
identifyUser:
|
|
323
331
|
options?.identifyUser ??
|
|
324
332
|
vi.fn().mockResolvedValue({ id: "user-1", name: "User One" }),
|
|
@@ -331,6 +339,7 @@ describe("handleRunAgent", () => {
|
|
|
331
339
|
clone: vi.fn(() => createClone()),
|
|
332
340
|
setMessages: vi.fn(),
|
|
333
341
|
setState: vi.fn(),
|
|
342
|
+
abortRun: vi.fn(),
|
|
334
343
|
threadId: undefined,
|
|
335
344
|
headers: {},
|
|
336
345
|
runAgent: vi.fn().mockResolvedValue(undefined),
|
|
@@ -340,6 +349,7 @@ describe("handleRunAgent", () => {
|
|
|
340
349
|
clone: vi.fn(() => createClone()),
|
|
341
350
|
setMessages: vi.fn(),
|
|
342
351
|
setState: vi.fn(),
|
|
352
|
+
abortRun: vi.fn(),
|
|
343
353
|
threadId: undefined,
|
|
344
354
|
headers: {},
|
|
345
355
|
runAgent: vi.fn().mockResolvedValue(undefined),
|
|
@@ -355,9 +365,12 @@ describe("handleRunAgent", () => {
|
|
|
355
365
|
created: false,
|
|
356
366
|
}),
|
|
357
367
|
getThreadMessages: vi.fn().mockResolvedValue({ messages: [] }),
|
|
358
|
-
ɵacquireThreadLock: vi
|
|
359
|
-
|
|
360
|
-
|
|
368
|
+
ɵacquireThreadLock: vi.fn().mockResolvedValue({
|
|
369
|
+
threadId: "thread-1",
|
|
370
|
+
runId: "run-1",
|
|
371
|
+
joinToken: "jt-123",
|
|
372
|
+
}),
|
|
373
|
+
ɵcleanupThreadLock: vi.fn().mockResolvedValue(undefined),
|
|
361
374
|
};
|
|
362
375
|
const runtime = createIntelligenceRuntime(agent, platform);
|
|
363
376
|
|
|
@@ -370,7 +383,15 @@ describe("handleRunAgent", () => {
|
|
|
370
383
|
expect(response.status).toBe(200);
|
|
371
384
|
expect(response.headers.get("Content-Type")).toBe("application/json");
|
|
372
385
|
const body = await response.json();
|
|
373
|
-
expect(body).toEqual({
|
|
386
|
+
expect(body).toEqual({
|
|
387
|
+
threadId: "thread-1",
|
|
388
|
+
runId: "run-1",
|
|
389
|
+
joinToken: "jt-123",
|
|
390
|
+
realtime: {
|
|
391
|
+
clientUrl: "wss://runtime.example/client",
|
|
392
|
+
topic: "thread:thread-1",
|
|
393
|
+
},
|
|
394
|
+
});
|
|
374
395
|
expect(platform.getOrCreateThread).toHaveBeenCalledWith({
|
|
375
396
|
threadId: "thread-1",
|
|
376
397
|
userId: "user-1",
|
|
@@ -380,6 +401,8 @@ describe("handleRunAgent", () => {
|
|
|
380
401
|
threadId: "thread-1",
|
|
381
402
|
runId: "run-1",
|
|
382
403
|
userId: "user-1",
|
|
404
|
+
agentId: "my-agent",
|
|
405
|
+
ttlSeconds: 20,
|
|
383
406
|
});
|
|
384
407
|
expect(platform.getThreadMessages).toHaveBeenCalledWith({
|
|
385
408
|
threadId: "thread-1",
|
|
@@ -394,9 +417,12 @@ describe("handleRunAgent", () => {
|
|
|
394
417
|
created: false,
|
|
395
418
|
}),
|
|
396
419
|
getThreadMessages: vi.fn().mockResolvedValue({ messages: [] }),
|
|
397
|
-
ɵacquireThreadLock: vi
|
|
398
|
-
|
|
399
|
-
|
|
420
|
+
ɵacquireThreadLock: vi.fn().mockResolvedValue({
|
|
421
|
+
threadId: "thread-1",
|
|
422
|
+
runId: "run-1",
|
|
423
|
+
joinToken: "jt-123",
|
|
424
|
+
}),
|
|
425
|
+
ɵcleanupThreadLock: vi.fn().mockResolvedValue(undefined),
|
|
400
426
|
};
|
|
401
427
|
const identifyUser = vi
|
|
402
428
|
.fn()
|
|
@@ -424,10 +450,12 @@ describe("handleRunAgent", () => {
|
|
|
424
450
|
threadId: "thread-1",
|
|
425
451
|
runId: "run-1",
|
|
426
452
|
userId: "resolved-user",
|
|
453
|
+
agentId: "my-agent",
|
|
454
|
+
ttlSeconds: 20,
|
|
427
455
|
});
|
|
428
456
|
});
|
|
429
457
|
|
|
430
|
-
it("
|
|
458
|
+
it("starts the runner with canonical threadId and runId from the lock response", async () => {
|
|
431
459
|
const agent = createAgentForIntelligence();
|
|
432
460
|
const platform = {
|
|
433
461
|
getOrCreateThread: vi.fn().mockResolvedValue({
|
|
@@ -435,9 +463,11 @@ describe("handleRunAgent", () => {
|
|
|
435
463
|
created: false,
|
|
436
464
|
}),
|
|
437
465
|
getThreadMessages: vi.fn().mockResolvedValue({ messages: [] }),
|
|
438
|
-
ɵacquireThreadLock: vi
|
|
439
|
-
|
|
440
|
-
|
|
466
|
+
ɵacquireThreadLock: vi.fn().mockResolvedValue({
|
|
467
|
+
threadId: "canonical-thread",
|
|
468
|
+
runId: "canonical-run",
|
|
469
|
+
joinToken: "jt-456",
|
|
470
|
+
}),
|
|
441
471
|
};
|
|
442
472
|
const runtime = createIntelligenceRuntime(agent, platform);
|
|
443
473
|
|
|
@@ -448,11 +478,17 @@ describe("handleRunAgent", () => {
|
|
|
448
478
|
});
|
|
449
479
|
|
|
450
480
|
expect(runtime.runner.run).toHaveBeenCalledWith(
|
|
451
|
-
expect.objectContaining({
|
|
481
|
+
expect.objectContaining({
|
|
482
|
+
threadId: "canonical-thread",
|
|
483
|
+
input: expect.objectContaining({
|
|
484
|
+
threadId: "canonical-thread",
|
|
485
|
+
runId: "canonical-run",
|
|
486
|
+
}),
|
|
487
|
+
}),
|
|
452
488
|
);
|
|
453
489
|
});
|
|
454
490
|
|
|
455
|
-
it("returns 502 when joinToken is missing", async () => {
|
|
491
|
+
it("cleans up the lock and returns 502 when joinToken is missing", async () => {
|
|
456
492
|
const agent = createAgentForIntelligence();
|
|
457
493
|
const platform = {
|
|
458
494
|
getOrCreateThread: vi.fn().mockResolvedValue({
|
|
@@ -460,7 +496,11 @@ describe("handleRunAgent", () => {
|
|
|
460
496
|
created: false,
|
|
461
497
|
}),
|
|
462
498
|
getThreadMessages: vi.fn().mockResolvedValue({ messages: [] }),
|
|
463
|
-
ɵacquireThreadLock: vi.fn().mockResolvedValue({
|
|
499
|
+
ɵacquireThreadLock: vi.fn().mockResolvedValue({
|
|
500
|
+
threadId: "thread-1",
|
|
501
|
+
runId: "run-1",
|
|
502
|
+
}),
|
|
503
|
+
ɵcleanupThreadLock: vi.fn().mockResolvedValue(undefined),
|
|
464
504
|
};
|
|
465
505
|
const runtime = createIntelligenceRuntime(agent, platform);
|
|
466
506
|
|
|
@@ -472,7 +512,40 @@ describe("handleRunAgent", () => {
|
|
|
472
512
|
|
|
473
513
|
expect(response.status).toBe(502);
|
|
474
514
|
const body = await response.json();
|
|
475
|
-
expect(body.error).toBe("
|
|
515
|
+
expect(body.error).toBe("Run connection credentials not available");
|
|
516
|
+
expect(platform.ɵcleanupThreadLock).toHaveBeenCalledWith({
|
|
517
|
+
threadId: "thread-1",
|
|
518
|
+
runId: "run-1",
|
|
519
|
+
});
|
|
520
|
+
expect(runtime.runner.run).not.toHaveBeenCalled();
|
|
521
|
+
});
|
|
522
|
+
|
|
523
|
+
it("uses the requested lock owner when malformed credentials omit canonical IDs", async () => {
|
|
524
|
+
const agent = createAgentForIntelligence();
|
|
525
|
+
const platform = {
|
|
526
|
+
getOrCreateThread: vi.fn().mockResolvedValue({
|
|
527
|
+
thread: { id: "thread-1", name: null },
|
|
528
|
+
created: false,
|
|
529
|
+
}),
|
|
530
|
+
getThreadMessages: vi.fn().mockResolvedValue({ messages: [] }),
|
|
531
|
+
ɵacquireThreadLock: vi.fn().mockResolvedValue({
|
|
532
|
+
joinToken: "jt-123",
|
|
533
|
+
}),
|
|
534
|
+
ɵcleanupThreadLock: vi.fn().mockResolvedValue(undefined),
|
|
535
|
+
};
|
|
536
|
+
const runtime = createIntelligenceRuntime(agent, platform);
|
|
537
|
+
|
|
538
|
+
const response = await handleRunAgent({
|
|
539
|
+
runtime,
|
|
540
|
+
request: createRunRequest(),
|
|
541
|
+
agentId: "my-agent",
|
|
542
|
+
});
|
|
543
|
+
|
|
544
|
+
expect(response.status).toBe(502);
|
|
545
|
+
expect(platform.ɵcleanupThreadLock).toHaveBeenCalledWith({
|
|
546
|
+
threadId: "thread-1",
|
|
547
|
+
runId: "run-1",
|
|
548
|
+
});
|
|
476
549
|
expect(runtime.runner.run).not.toHaveBeenCalled();
|
|
477
550
|
});
|
|
478
551
|
|
|
@@ -501,6 +574,206 @@ describe("handleRunAgent", () => {
|
|
|
501
574
|
expect(body.error).toBe("Thread lock denied");
|
|
502
575
|
});
|
|
503
576
|
|
|
577
|
+
it("cleans up the canonical lock and returns 502 when runner start fails immediately", async () => {
|
|
578
|
+
const agent = createAgentForIntelligence();
|
|
579
|
+
const platform = {
|
|
580
|
+
getOrCreateThread: vi.fn().mockResolvedValue({
|
|
581
|
+
thread: { id: "thread-1", name: null },
|
|
582
|
+
created: false,
|
|
583
|
+
}),
|
|
584
|
+
getThreadMessages: vi.fn().mockResolvedValue({ messages: [] }),
|
|
585
|
+
ɵacquireThreadLock: vi.fn().mockResolvedValue({
|
|
586
|
+
threadId: "canonical-thread",
|
|
587
|
+
runId: "canonical-run",
|
|
588
|
+
joinToken: "jt-123",
|
|
589
|
+
}),
|
|
590
|
+
ɵcleanupThreadLock: vi.fn().mockResolvedValue(undefined),
|
|
591
|
+
};
|
|
592
|
+
const runtime = createIntelligenceRuntime(agent, platform);
|
|
593
|
+
runtime.runner.run = vi.fn(
|
|
594
|
+
() =>
|
|
595
|
+
new Observable<BaseEvent>((subscriber) => {
|
|
596
|
+
subscriber.next({
|
|
597
|
+
type: EventType.RUN_ERROR,
|
|
598
|
+
message: "join failed",
|
|
599
|
+
} as BaseEvent);
|
|
600
|
+
subscriber.complete();
|
|
601
|
+
}),
|
|
602
|
+
);
|
|
603
|
+
|
|
604
|
+
const response = await handleRunAgent({
|
|
605
|
+
runtime,
|
|
606
|
+
request: createRunRequest(),
|
|
607
|
+
agentId: "my-agent",
|
|
608
|
+
});
|
|
609
|
+
|
|
610
|
+
expect(response.status).toBe(502);
|
|
611
|
+
const body = await response.json();
|
|
612
|
+
expect(body).toEqual({
|
|
613
|
+
error: "Failed to start runner",
|
|
614
|
+
message: "join failed",
|
|
615
|
+
});
|
|
616
|
+
expect(platform.ɵcleanupThreadLock).toHaveBeenCalledWith({
|
|
617
|
+
threadId: "canonical-thread",
|
|
618
|
+
runId: "canonical-run",
|
|
619
|
+
});
|
|
620
|
+
expect(platform.ɵcleanupThreadLock).toHaveBeenCalledTimes(1);
|
|
621
|
+
});
|
|
622
|
+
|
|
623
|
+
it("delays the run success response until the runner startup boundary resolves", async () => {
|
|
624
|
+
const agent = createAgentForIntelligence();
|
|
625
|
+
let resolveStartup: (() => void) | undefined;
|
|
626
|
+
const startup = new Promise<void>((resolve) => {
|
|
627
|
+
resolveStartup = resolve;
|
|
628
|
+
});
|
|
629
|
+
const platform = {
|
|
630
|
+
getOrCreateThread: vi.fn().mockResolvedValue({
|
|
631
|
+
thread: { id: "thread-1", name: null },
|
|
632
|
+
created: false,
|
|
633
|
+
}),
|
|
634
|
+
getThreadMessages: vi.fn().mockResolvedValue({ messages: [] }),
|
|
635
|
+
ɵacquireThreadLock: vi.fn().mockResolvedValue({
|
|
636
|
+
threadId: "canonical-thread",
|
|
637
|
+
runId: "canonical-run",
|
|
638
|
+
joinToken: "jt-123",
|
|
639
|
+
}),
|
|
640
|
+
ɵcleanupThreadLock: vi.fn().mockResolvedValue(undefined),
|
|
641
|
+
};
|
|
642
|
+
const runtime = createIntelligenceRuntime(agent, platform);
|
|
643
|
+
runtime.runner.runWithStartupBoundary = vi.fn(() => ({
|
|
644
|
+
events: new Observable<BaseEvent>(() => {}),
|
|
645
|
+
startup,
|
|
646
|
+
}));
|
|
647
|
+
let settled = false;
|
|
648
|
+
|
|
649
|
+
const responsePromise = handleRunAgent({
|
|
650
|
+
runtime,
|
|
651
|
+
request: createRunRequest(),
|
|
652
|
+
agentId: "my-agent",
|
|
653
|
+
}).then((response) => {
|
|
654
|
+
settled = true;
|
|
655
|
+
return response;
|
|
656
|
+
});
|
|
657
|
+
|
|
658
|
+
await Promise.resolve();
|
|
659
|
+
|
|
660
|
+
expect(settled).toBe(false);
|
|
661
|
+
|
|
662
|
+
resolveStartup?.();
|
|
663
|
+
const response = await responsePromise;
|
|
664
|
+
|
|
665
|
+
expect(response.status).toBe(200);
|
|
666
|
+
expect(runtime.runner.runWithStartupBoundary).toHaveBeenCalledWith(
|
|
667
|
+
expect.objectContaining({
|
|
668
|
+
threadId: "canonical-thread",
|
|
669
|
+
input: expect.objectContaining({
|
|
670
|
+
threadId: "canonical-thread",
|
|
671
|
+
runId: "canonical-run",
|
|
672
|
+
}),
|
|
673
|
+
}),
|
|
674
|
+
);
|
|
675
|
+
});
|
|
676
|
+
|
|
677
|
+
it("cleans up the lock and returns 502 when the runner startup boundary rejects", async () => {
|
|
678
|
+
const agent = createAgentForIntelligence();
|
|
679
|
+
const platform = {
|
|
680
|
+
getOrCreateThread: vi.fn().mockResolvedValue({
|
|
681
|
+
thread: { id: "thread-1", name: null },
|
|
682
|
+
created: false,
|
|
683
|
+
}),
|
|
684
|
+
getThreadMessages: vi.fn().mockResolvedValue({ messages: [] }),
|
|
685
|
+
ɵacquireThreadLock: vi.fn().mockResolvedValue({
|
|
686
|
+
threadId: "canonical-thread",
|
|
687
|
+
runId: "canonical-run",
|
|
688
|
+
joinToken: "jt-123",
|
|
689
|
+
}),
|
|
690
|
+
ɵcleanupThreadLock: vi.fn().mockResolvedValue(undefined),
|
|
691
|
+
};
|
|
692
|
+
const runtime = createIntelligenceRuntime(agent, platform);
|
|
693
|
+
runtime.runner.runWithStartupBoundary = vi.fn(() => ({
|
|
694
|
+
events: new Observable<BaseEvent>(() => {}),
|
|
695
|
+
startup: Promise.reject(new Error("Failed to join channel: denied")),
|
|
696
|
+
}));
|
|
697
|
+
|
|
698
|
+
const response = await handleRunAgent({
|
|
699
|
+
runtime,
|
|
700
|
+
request: createRunRequest(),
|
|
701
|
+
agentId: "my-agent",
|
|
702
|
+
});
|
|
703
|
+
|
|
704
|
+
expect(response.status).toBe(502);
|
|
705
|
+
const body = await response.json();
|
|
706
|
+
expect(body).toEqual({
|
|
707
|
+
error: "Failed to start runner",
|
|
708
|
+
message: "Failed to join channel: denied",
|
|
709
|
+
});
|
|
710
|
+
expect(platform.ɵcleanupThreadLock).toHaveBeenCalledWith({
|
|
711
|
+
threadId: "canonical-thread",
|
|
712
|
+
runId: "canonical-run",
|
|
713
|
+
});
|
|
714
|
+
expect(platform.ɵcleanupThreadLock).toHaveBeenCalledTimes(1);
|
|
715
|
+
});
|
|
716
|
+
|
|
717
|
+
it("aborts the agent when lock renewal fails", async () => {
|
|
718
|
+
vi.useFakeTimers();
|
|
719
|
+
const runningAgent = {
|
|
720
|
+
clone: vi.fn(),
|
|
721
|
+
setMessages: vi.fn(),
|
|
722
|
+
setState: vi.fn(),
|
|
723
|
+
abortRun: vi.fn(),
|
|
724
|
+
threadId: undefined,
|
|
725
|
+
headers: {},
|
|
726
|
+
runAgent: vi.fn().mockResolvedValue(undefined),
|
|
727
|
+
} as unknown as AbstractAgent;
|
|
728
|
+
const baseAgent = {
|
|
729
|
+
clone: vi.fn(() => runningAgent),
|
|
730
|
+
setMessages: vi.fn(),
|
|
731
|
+
setState: vi.fn(),
|
|
732
|
+
abortRun: vi.fn(),
|
|
733
|
+
threadId: undefined,
|
|
734
|
+
headers: {},
|
|
735
|
+
runAgent: vi.fn().mockResolvedValue(undefined),
|
|
736
|
+
} as unknown as AbstractAgent;
|
|
737
|
+
const platform = {
|
|
738
|
+
getOrCreateThread: vi.fn().mockResolvedValue({
|
|
739
|
+
thread: { id: "thread-1", name: null },
|
|
740
|
+
created: false,
|
|
741
|
+
}),
|
|
742
|
+
getThreadMessages: vi.fn().mockResolvedValue({ messages: [] }),
|
|
743
|
+
ɵacquireThreadLock: vi.fn().mockResolvedValue({
|
|
744
|
+
threadId: "canonical-thread",
|
|
745
|
+
runId: "canonical-run",
|
|
746
|
+
joinToken: "jt-123",
|
|
747
|
+
}),
|
|
748
|
+
ɵrenewThreadLock: vi.fn().mockRejectedValue(new Error("lost lock")),
|
|
749
|
+
};
|
|
750
|
+
const runtime = createIntelligenceRuntime(baseAgent, platform, {
|
|
751
|
+
lockHeartbeatIntervalSeconds: 1,
|
|
752
|
+
lockTtlSeconds: 5,
|
|
753
|
+
});
|
|
754
|
+
runtime.runner.run = vi.fn(() => new Observable<BaseEvent>(() => {}));
|
|
755
|
+
|
|
756
|
+
try {
|
|
757
|
+
const response = await handleRunAgent({
|
|
758
|
+
runtime,
|
|
759
|
+
request: createRunRequest(),
|
|
760
|
+
agentId: "my-agent",
|
|
761
|
+
});
|
|
762
|
+
expect(response.status).toBe(200);
|
|
763
|
+
|
|
764
|
+
await vi.advanceTimersByTimeAsync(1_000);
|
|
765
|
+
|
|
766
|
+
expect(platform.ɵrenewThreadLock).toHaveBeenCalledWith({
|
|
767
|
+
threadId: "canonical-thread",
|
|
768
|
+
runId: "canonical-run",
|
|
769
|
+
ttlSeconds: 5,
|
|
770
|
+
});
|
|
771
|
+
expect(runningAgent.abortRun).toHaveBeenCalledTimes(1);
|
|
772
|
+
} finally {
|
|
773
|
+
vi.useRealTimers();
|
|
774
|
+
}
|
|
775
|
+
});
|
|
776
|
+
|
|
504
777
|
it("passes only unseen input messages to the runner for durable persistence", async () => {
|
|
505
778
|
const agent = createAgentForIntelligence();
|
|
506
779
|
const platform = {
|
|
@@ -517,9 +790,11 @@ describe("handleRunAgent", () => {
|
|
|
517
790
|
},
|
|
518
791
|
],
|
|
519
792
|
}),
|
|
520
|
-
ɵacquireThreadLock: vi
|
|
521
|
-
|
|
522
|
-
|
|
793
|
+
ɵacquireThreadLock: vi.fn().mockResolvedValue({
|
|
794
|
+
threadId: "thread-1",
|
|
795
|
+
runId: "run-1",
|
|
796
|
+
joinToken: "jt-123",
|
|
797
|
+
}),
|
|
523
798
|
};
|
|
524
799
|
const runtime = createIntelligenceRuntime(agent, platform);
|
|
525
800
|
const response = await handleRunAgent({
|
|
@@ -577,9 +852,12 @@ describe("handleRunAgent", () => {
|
|
|
577
852
|
getThreadMessages: vi
|
|
578
853
|
.fn()
|
|
579
854
|
.mockRejectedValue(new Error("history unavailable")),
|
|
580
|
-
ɵacquireThreadLock: vi
|
|
581
|
-
|
|
582
|
-
|
|
855
|
+
ɵacquireThreadLock: vi.fn().mockResolvedValue({
|
|
856
|
+
threadId: "thread-1",
|
|
857
|
+
runId: "run-1",
|
|
858
|
+
joinToken: "jt-123",
|
|
859
|
+
}),
|
|
860
|
+
ɵcleanupThreadLock: vi.fn().mockResolvedValue(undefined),
|
|
583
861
|
};
|
|
584
862
|
const runtime = createIntelligenceRuntime(agent, platform);
|
|
585
863
|
|
|
@@ -592,6 +870,10 @@ describe("handleRunAgent", () => {
|
|
|
592
870
|
expect(response.status).toBe(502);
|
|
593
871
|
const body = await response.json();
|
|
594
872
|
expect(body.error).toBe("Thread history lookup failed");
|
|
873
|
+
expect(platform.ɵcleanupThreadLock).toHaveBeenCalledWith({
|
|
874
|
+
threadId: "thread-1",
|
|
875
|
+
runId: "run-1",
|
|
876
|
+
});
|
|
595
877
|
expect(runtime.runner.run).not.toHaveBeenCalled();
|
|
596
878
|
});
|
|
597
879
|
|
|
@@ -604,8 +886,9 @@ describe("handleRunAgent", () => {
|
|
|
604
886
|
}),
|
|
605
887
|
getThreadMessages: vi.fn().mockResolvedValue({ messages: [] }),
|
|
606
888
|
ɵacquireThreadLock: vi.fn().mockResolvedValue({
|
|
889
|
+
threadId: "thread-1",
|
|
890
|
+
runId: "run-1",
|
|
607
891
|
joinToken: "jt-created",
|
|
608
|
-
joinCode: "jc-created",
|
|
609
892
|
}),
|
|
610
893
|
};
|
|
611
894
|
const runtime = createIntelligenceRuntime(agent, platform);
|
|
@@ -626,6 +909,8 @@ describe("handleRunAgent", () => {
|
|
|
626
909
|
threadId: "thread-1",
|
|
627
910
|
runId: "run-1",
|
|
628
911
|
userId: "user-1",
|
|
912
|
+
agentId: "my-agent",
|
|
913
|
+
ttlSeconds: 20,
|
|
629
914
|
});
|
|
630
915
|
});
|
|
631
916
|
|
|
@@ -675,8 +960,9 @@ describe("handleRunAgent", () => {
|
|
|
675
960
|
}),
|
|
676
961
|
getThreadMessages: vi.fn().mockResolvedValue({ messages: [] }),
|
|
677
962
|
ɵacquireThreadLock: vi.fn().mockResolvedValue({
|
|
963
|
+
threadId: "thread-1",
|
|
964
|
+
runId: "run-1",
|
|
678
965
|
joinToken: "jt-created",
|
|
679
|
-
joinCode: "jc-created",
|
|
680
966
|
}),
|
|
681
967
|
};
|
|
682
968
|
const runtime = createIntelligenceRuntime(baseAgent, platform, {
|
|
@@ -730,8 +1016,9 @@ describe("handleRunAgent", () => {
|
|
|
730
1016
|
updateThread: vi.fn(),
|
|
731
1017
|
getThreadMessages: vi.fn().mockResolvedValue({ messages: [] }),
|
|
732
1018
|
ɵacquireThreadLock: vi.fn().mockResolvedValue({
|
|
1019
|
+
threadId: "thread-1",
|
|
1020
|
+
runId: "run-1",
|
|
733
1021
|
joinToken: "jt-created",
|
|
734
|
-
joinCode: "jc-created",
|
|
735
1022
|
}),
|
|
736
1023
|
};
|
|
737
1024
|
const runtime = createIntelligenceRuntime(agent, platform, {
|
|
@@ -759,8 +1046,9 @@ describe("handleRunAgent", () => {
|
|
|
759
1046
|
updateThread: vi.fn(),
|
|
760
1047
|
getThreadMessages: vi.fn().mockResolvedValue({ messages: [] }),
|
|
761
1048
|
ɵacquireThreadLock: vi.fn().mockResolvedValue({
|
|
1049
|
+
threadId: "thread-1",
|
|
1050
|
+
runId: "run-1",
|
|
762
1051
|
joinToken: "jt-created",
|
|
763
|
-
joinCode: "jc-created",
|
|
764
1052
|
}),
|
|
765
1053
|
};
|
|
766
1054
|
const runtime = createIntelligenceRuntime(agent, platform, {
|
|
@@ -815,8 +1103,9 @@ describe("handleRunAgent", () => {
|
|
|
815
1103
|
updateThread: vi.fn(),
|
|
816
1104
|
getThreadMessages: vi.fn().mockResolvedValue({ messages: [] }),
|
|
817
1105
|
ɵacquireThreadLock: vi.fn().mockResolvedValue({
|
|
1106
|
+
threadId: "thread-1",
|
|
1107
|
+
runId: "run-1",
|
|
818
1108
|
joinToken: "jt-created",
|
|
819
|
-
joinCode: "jc-created",
|
|
820
1109
|
}),
|
|
821
1110
|
};
|
|
822
1111
|
const runtime = createIntelligenceRuntime(baseAgent, platform, {
|
|
@@ -938,4 +1227,66 @@ describe("handleRunAgent", () => {
|
|
|
938
1227
|
}
|
|
939
1228
|
});
|
|
940
1229
|
});
|
|
1230
|
+
|
|
1231
|
+
describe("telemetry", () => {
|
|
1232
|
+
it("captures oss.runtime.copilot_request_created on every invocation", async () => {
|
|
1233
|
+
// Dynamic import so we spy on the module singleton the handler uses.
|
|
1234
|
+
const { telemetry } = await import("../telemetry");
|
|
1235
|
+
const captureSpy = vi
|
|
1236
|
+
.spyOn(telemetry, "capture")
|
|
1237
|
+
.mockResolvedValue(undefined);
|
|
1238
|
+
|
|
1239
|
+
try {
|
|
1240
|
+
const runtime = createMockRuntime({});
|
|
1241
|
+
await handleRunAgent({
|
|
1242
|
+
runtime,
|
|
1243
|
+
request: createMockRequest(),
|
|
1244
|
+
agentId: "nonexistent-agent",
|
|
1245
|
+
});
|
|
1246
|
+
|
|
1247
|
+
expect(captureSpy).toHaveBeenCalledWith(
|
|
1248
|
+
"oss.runtime.copilot_request_created",
|
|
1249
|
+
expect.objectContaining({
|
|
1250
|
+
requestType: "run",
|
|
1251
|
+
"cloud.api_key_provided": false,
|
|
1252
|
+
}),
|
|
1253
|
+
);
|
|
1254
|
+
} finally {
|
|
1255
|
+
captureSpy.mockRestore();
|
|
1256
|
+
}
|
|
1257
|
+
});
|
|
1258
|
+
|
|
1259
|
+
it("includes cloud.public_api_key when x-copilotcloud-public-api-key header is set", async () => {
|
|
1260
|
+
const { telemetry } = await import("../telemetry");
|
|
1261
|
+
const captureSpy = vi
|
|
1262
|
+
.spyOn(telemetry, "capture")
|
|
1263
|
+
.mockResolvedValue(undefined);
|
|
1264
|
+
|
|
1265
|
+
try {
|
|
1266
|
+
const runtime = createMockRuntime({});
|
|
1267
|
+
const request = new Request("https://example.com/agent/test/run", {
|
|
1268
|
+
method: "POST",
|
|
1269
|
+
headers: {
|
|
1270
|
+
"x-copilotcloud-public-api-key": "ck_pub_run_test",
|
|
1271
|
+
},
|
|
1272
|
+
});
|
|
1273
|
+
|
|
1274
|
+
await handleRunAgent({
|
|
1275
|
+
runtime,
|
|
1276
|
+
request,
|
|
1277
|
+
agentId: "nonexistent-agent",
|
|
1278
|
+
});
|
|
1279
|
+
|
|
1280
|
+
expect(captureSpy).toHaveBeenCalledWith(
|
|
1281
|
+
"oss.runtime.copilot_request_created",
|
|
1282
|
+
expect.objectContaining({
|
|
1283
|
+
"cloud.api_key_provided": true,
|
|
1284
|
+
"cloud.public_api_key": "ck_pub_run_test",
|
|
1285
|
+
}),
|
|
1286
|
+
);
|
|
1287
|
+
} finally {
|
|
1288
|
+
captureSpy.mockRestore();
|
|
1289
|
+
}
|
|
1290
|
+
});
|
|
1291
|
+
});
|
|
941
1292
|
});
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Integration test: Hono single-route adapter (deprecated path) + telemetry.
|
|
3
|
+
*
|
|
4
|
+
* `createCopilotEndpointSingleRoute` is the legacy direct single-route entry
|
|
5
|
+
* point, superseded by `createCopilotHonoHandler({ mode: "single-route" })`
|
|
6
|
+
* but still exported.
|
|
7
|
+
*/
|
|
8
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
|
9
|
+
import type { AbstractAgent } from "@ag-ui/client";
|
|
10
|
+
|
|
11
|
+
import { telemetry } from "../telemetry";
|
|
12
|
+
import { createCopilotEndpointSingleRoute } from "../endpoints/hono-single";
|
|
13
|
+
import { CopilotRuntime } from "../core/runtime";
|
|
14
|
+
|
|
15
|
+
function makeAgent(): AbstractAgent {
|
|
16
|
+
const a: unknown = { execute: async () => ({ events: [] }) };
|
|
17
|
+
(a as { clone: () => unknown }).clone = () => makeAgent();
|
|
18
|
+
return a as AbstractAgent;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
describe("Hono single-route adapter — telemetry firing (integration)", () => {
|
|
22
|
+
let captureSpy: ReturnType<typeof vi.spyOn>;
|
|
23
|
+
|
|
24
|
+
beforeEach(() => {
|
|
25
|
+
captureSpy = vi.spyOn(telemetry, "capture").mockResolvedValue(undefined);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
afterEach(() => {
|
|
29
|
+
captureSpy.mockRestore();
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it("fires instance_created on handler creation", async () => {
|
|
33
|
+
const runtime = new CopilotRuntime({ agents: { default: makeAgent() } });
|
|
34
|
+
createCopilotEndpointSingleRoute({ runtime, basePath: "/api/copilotkit" });
|
|
35
|
+
|
|
36
|
+
await vi.waitFor(() => {
|
|
37
|
+
expect(captureSpy).toHaveBeenCalledWith(
|
|
38
|
+
"oss.runtime.instance_created",
|
|
39
|
+
expect.objectContaining({
|
|
40
|
+
agentsAmount: 1,
|
|
41
|
+
"cloud.api_key_provided": false,
|
|
42
|
+
}),
|
|
43
|
+
);
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
});
|