@copilotkit/web-inspector 1.56.5 → 1.57.0
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/index.cjs +2763 -199
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +265 -10
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +265 -10
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +2764 -201
- package/dist/index.mjs.map +1 -1
- package/dist/index.umd.js +2801 -211
- package/dist/index.umd.js.map +1 -1
- package/dist/styles/generated.cjs +1 -1
- package/dist/styles/generated.cjs.map +1 -1
- package/dist/styles/generated.mjs +1 -1
- package/dist/styles/generated.mjs.map +1 -1
- package/package.json +2 -2
- package/src/__tests__/web-inspector.spec.ts +179 -1
- package/src/index.ts +3474 -300
- package/src/styles/generated.css +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { WebInspectorElement } from "../index";
|
|
1
|
+
import { WebInspectorElement, ɵCpkThreadDetails } from "../index";
|
|
2
2
|
import {
|
|
3
3
|
CopilotKitCore,
|
|
4
4
|
CopilotKitCoreRuntimeConnectionStatus,
|
|
@@ -94,6 +94,8 @@ type MockCore = {
|
|
|
94
94
|
subscribe: (subscriber: CopilotKitCoreSubscriber) => {
|
|
95
95
|
unsubscribe: () => void;
|
|
96
96
|
};
|
|
97
|
+
getThreadStores: () => Record<string, never>;
|
|
98
|
+
getThreadStore: (agentId: string) => undefined;
|
|
97
99
|
};
|
|
98
100
|
|
|
99
101
|
function createMockCore(initialAgents: Record<string, AbstractAgent> = {}) {
|
|
@@ -107,6 +109,12 @@ function createMockCore(initialAgents: Record<string, AbstractAgent> = {}) {
|
|
|
107
109
|
subscribers.add(subscriber);
|
|
108
110
|
return { unsubscribe: () => subscribers.delete(subscriber) };
|
|
109
111
|
},
|
|
112
|
+
getThreadStores() {
|
|
113
|
+
return {};
|
|
114
|
+
},
|
|
115
|
+
getThreadStore(_agentId: string) {
|
|
116
|
+
return undefined;
|
|
117
|
+
},
|
|
110
118
|
};
|
|
111
119
|
|
|
112
120
|
return {
|
|
@@ -284,3 +292,173 @@ describe("WebInspectorElement", () => {
|
|
|
284
292
|
expect(internals.agentStates.get("counter")).toEqual({ counter: 5 });
|
|
285
293
|
});
|
|
286
294
|
});
|
|
295
|
+
|
|
296
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
297
|
+
// CpkThreadDetails — per-panel TemplateResult cache invariants
|
|
298
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
299
|
+
//
|
|
300
|
+
// The conversation / agent-state / events panels each cache the rendered
|
|
301
|
+
// TemplateResult by reference of the underlying data so tab switches don't
|
|
302
|
+
// re-iterate over hundreds of items. These tests pin down the cache-key
|
|
303
|
+
// contract: when the data reassigns, the cache MUST drop, and when the
|
|
304
|
+
// thread changes, ALL three caches MUST reset. A future edit that mutates
|
|
305
|
+
// the data in place (instead of reassigning) or forgets to null one of the
|
|
306
|
+
// caches in `updated()` would silently render stale content under a new
|
|
307
|
+
// thread, which is undetectable by manual QA on a single thread.
|
|
308
|
+
|
|
309
|
+
type ThreadDetailsInternals = {
|
|
310
|
+
threadId: string | null;
|
|
311
|
+
liveMessageVersion: number;
|
|
312
|
+
_conversation: Array<Record<string, unknown>>;
|
|
313
|
+
_fetchedState: Record<string, unknown> | null;
|
|
314
|
+
_fetchedEvents: Array<unknown> | null;
|
|
315
|
+
_expandedTools: Set<string>;
|
|
316
|
+
_expandedMessages: Set<string>;
|
|
317
|
+
_stateNotAvailable: boolean;
|
|
318
|
+
_eventsNotAvailable: boolean;
|
|
319
|
+
_loadingMessages: boolean;
|
|
320
|
+
_loadingState: boolean;
|
|
321
|
+
_loadingEvents: boolean;
|
|
322
|
+
_panelTplCache: Map<string, { key: readonly unknown[]; tpl: unknown }>;
|
|
323
|
+
renderConversation: () => unknown;
|
|
324
|
+
renderState: () => unknown;
|
|
325
|
+
renderEvents: () => unknown;
|
|
326
|
+
};
|
|
327
|
+
|
|
328
|
+
function createThreadDetails(): {
|
|
329
|
+
el: ɵCpkThreadDetails;
|
|
330
|
+
internals: ThreadDetailsInternals;
|
|
331
|
+
} {
|
|
332
|
+
const el = new ɵCpkThreadDetails();
|
|
333
|
+
document.body.appendChild(el);
|
|
334
|
+
// Same cast pattern as `getInternals` above — there's no public surface
|
|
335
|
+
// for the cache slots, so the test reaches through a typed view.
|
|
336
|
+
const internals = el as unknown as ThreadDetailsInternals;
|
|
337
|
+
return { el, internals };
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
describe("ɵCpkThreadDetails caching", () => {
|
|
341
|
+
beforeEach(() => {
|
|
342
|
+
document.body.innerHTML = "";
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Drive the threadId-change `updated()` block once so its reset path
|
|
347
|
+
* runs on entry, then seed the data the test cares about AFTER. If we
|
|
348
|
+
* seed before the first updateComplete, `updated()` immediately nulls
|
|
349
|
+
* `_fetchedState` / `_fetchedEvents` / `_conversation` (and
|
|
350
|
+
* `fetchMessages` re-clears `_conversation` when no `runtimeUrl` is
|
|
351
|
+
* configured, as in this jsdom test), so the assertions below would
|
|
352
|
+
* be running against an empty element.
|
|
353
|
+
*/
|
|
354
|
+
async function settleThread(
|
|
355
|
+
el: ɵCpkThreadDetails,
|
|
356
|
+
internals: ThreadDetailsInternals,
|
|
357
|
+
threadId: string,
|
|
358
|
+
): Promise<void> {
|
|
359
|
+
internals.threadId = threadId;
|
|
360
|
+
await el.updateComplete;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
it("threadId change drops all three template caches", async () => {
|
|
364
|
+
const { el, internals } = createThreadDetails();
|
|
365
|
+
await settleThread(el, internals, "t1");
|
|
366
|
+
|
|
367
|
+
// Hand-build cache entries for all three panels so we don't have to
|
|
368
|
+
// drive every render path through the DOM. The presence of any entry
|
|
369
|
+
// is what the assertion below checks for; what they hold is irrelevant.
|
|
370
|
+
internals._panelTplCache.set("conversation", { key: [], tpl: "c" });
|
|
371
|
+
internals._panelTplCache.set("agent-state", { key: [], tpl: "s" });
|
|
372
|
+
internals._panelTplCache.set("ag-ui-events", { key: [], tpl: "e" });
|
|
373
|
+
|
|
374
|
+
// Switch to thread t2 — the threadId branch in `updated()` should
|
|
375
|
+
// empty the cache map.
|
|
376
|
+
internals.threadId = "t2";
|
|
377
|
+
await el.updateComplete;
|
|
378
|
+
|
|
379
|
+
expect(internals._panelTplCache.size).toBe(0);
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
it("conversation cache invalidates when _conversation is reassigned", async () => {
|
|
383
|
+
const { el, internals } = createThreadDetails();
|
|
384
|
+
await settleThread(el, internals, "t1");
|
|
385
|
+
|
|
386
|
+
internals._conversation = [
|
|
387
|
+
{ id: "m1", type: "user", content: "hi", createdAt: "" },
|
|
388
|
+
];
|
|
389
|
+
|
|
390
|
+
const tplA = internals.renderConversation();
|
|
391
|
+
expect(internals._panelTplCache.get("conversation")?.tpl).toBe(tplA);
|
|
392
|
+
|
|
393
|
+
// Cache hit: same array reference, same expand sets — same TemplateResult.
|
|
394
|
+
expect(internals.renderConversation()).toBe(tplA);
|
|
395
|
+
|
|
396
|
+
// New array reference (the streaming refetch path always reassigns
|
|
397
|
+
// via `this._conversation = this.mapMessages(...)` rather than
|
|
398
|
+
// mutating in place — that contract is what this test pins).
|
|
399
|
+
internals._conversation = [
|
|
400
|
+
{ id: "m1", type: "user", content: "hi", createdAt: "" },
|
|
401
|
+
{ id: "m2", type: "assistant", content: "hello", createdAt: "" },
|
|
402
|
+
];
|
|
403
|
+
|
|
404
|
+
const tplB = internals.renderConversation();
|
|
405
|
+
expect(tplB).not.toBe(tplA);
|
|
406
|
+
expect(internals._panelTplCache.get("conversation")?.tpl).toBe(tplB);
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
it("conversation cache invalidates when expand state changes", async () => {
|
|
410
|
+
// Regression guard: an earlier version keyed the cache only on
|
|
411
|
+
// `_conversation`, so toggling a tool-call expand or a "Show more"
|
|
412
|
+
// on a long message returned the pre-toggle template. The cache key
|
|
413
|
+
// now includes both expand sets.
|
|
414
|
+
const { el, internals } = createThreadDetails();
|
|
415
|
+
await settleThread(el, internals, "t1");
|
|
416
|
+
|
|
417
|
+
internals._conversation = [
|
|
418
|
+
{
|
|
419
|
+
id: "tc1",
|
|
420
|
+
type: "tool_call",
|
|
421
|
+
toolName: "doThing",
|
|
422
|
+
toolCallId: "tc1",
|
|
423
|
+
arguments: { x: 1 },
|
|
424
|
+
result: null,
|
|
425
|
+
createdAt: "",
|
|
426
|
+
},
|
|
427
|
+
];
|
|
428
|
+
|
|
429
|
+
const collapsed = internals.renderConversation();
|
|
430
|
+
expect(internals.renderConversation()).toBe(collapsed);
|
|
431
|
+
|
|
432
|
+
// Simulating `toggleToolExpand("tc1")` — production code always
|
|
433
|
+
// builds a fresh Set, so reference equality flips.
|
|
434
|
+
internals._expandedTools = new Set(["tc1"]);
|
|
435
|
+
|
|
436
|
+
const expanded = internals.renderConversation();
|
|
437
|
+
expect(expanded).not.toBe(collapsed);
|
|
438
|
+
|
|
439
|
+
// Same for the long-message "Show more" path.
|
|
440
|
+
internals._expandedMessages = new Set(["m1"]);
|
|
441
|
+
|
|
442
|
+
expect(internals.renderConversation()).not.toBe(expanded);
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
it("state and events caches invalidate when their fetched data is reassigned", async () => {
|
|
446
|
+
const { el, internals } = createThreadDetails();
|
|
447
|
+
await settleThread(el, internals, "t1");
|
|
448
|
+
|
|
449
|
+
internals._fetchedState = { foo: "bar" };
|
|
450
|
+
internals._fetchedEvents = [{ type: "RUN_STARTED" }];
|
|
451
|
+
|
|
452
|
+
const stateA = internals.renderState();
|
|
453
|
+
const eventsA = internals.renderEvents();
|
|
454
|
+
expect(internals.renderState()).toBe(stateA);
|
|
455
|
+
expect(internals.renderEvents()).toBe(eventsA);
|
|
456
|
+
|
|
457
|
+
// Reassign both — fresh references after a refetch.
|
|
458
|
+
internals._fetchedState = { foo: "baz" };
|
|
459
|
+
internals._fetchedEvents = [{ type: "RUN_FINISHED" }];
|
|
460
|
+
|
|
461
|
+
expect(internals.renderState()).not.toBe(stateA);
|
|
462
|
+
expect(internals.renderEvents()).not.toBe(eventsA);
|
|
463
|
+
});
|
|
464
|
+
});
|