@copilotkit/web-inspector 1.62.2 → 1.63.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 +1671 -305
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +66 -0
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +66 -0
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1673 -307
- package/dist/index.mjs.map +1 -1
- package/dist/index.umd.js +1715 -294
- package/dist/index.umd.js.map +1 -1
- package/dist/lib/telemetry.cjs +42 -2
- package/dist/lib/telemetry.cjs.map +1 -1
- package/dist/lib/telemetry.mjs +35 -3
- package/dist/lib/telemetry.mjs.map +1 -1
- package/dist/package.cjs +1 -1
- package/dist/package.mjs +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 +1551 -7
- package/src/index.ts +1929 -225
- package/src/lib/telemetry.ts +79 -1
- package/src/styles/generated.css +1 -1
package/src/index.ts
CHANGED
|
@@ -13,12 +13,19 @@ import {
|
|
|
13
13
|
ɵselectThreads,
|
|
14
14
|
ɵselectThreadsError,
|
|
15
15
|
ɵcreateThreadStore,
|
|
16
|
+
ɵselectMemories,
|
|
17
|
+
ɵselectMemoriesIsLoading,
|
|
18
|
+
ɵselectMemoriesError,
|
|
19
|
+
ɵselectMemoriesAvailable,
|
|
20
|
+
ɵselectMemoriesRealtimeStatus,
|
|
16
21
|
} from "@copilotkit/core";
|
|
17
22
|
import type {
|
|
18
23
|
CopilotKitCoreSubscriber,
|
|
19
24
|
CopilotKitCoreErrorCode,
|
|
20
25
|
ɵThreadStore,
|
|
21
26
|
ɵThread,
|
|
27
|
+
Memory,
|
|
28
|
+
MemoryRealtimeStatus,
|
|
22
29
|
} from "@copilotkit/core";
|
|
23
30
|
import type { AbstractAgent, AgentSubscriber } from "@ag-ui/client";
|
|
24
31
|
import type {
|
|
@@ -58,12 +65,23 @@ import {
|
|
|
58
65
|
trackTalkToEngineerClicked,
|
|
59
66
|
trackThreadsEmptyEnabledViewed,
|
|
60
67
|
trackThreadsEnabledViewed,
|
|
68
|
+
trackThreadsExampleSelected,
|
|
69
|
+
trackThreadsExampleTourCompleted,
|
|
70
|
+
trackThreadsExampleTourDismissed,
|
|
71
|
+
trackThreadsExampleTourReopened,
|
|
72
|
+
trackThreadsExampleTourStarted,
|
|
73
|
+
trackThreadsExampleTourStepViewed,
|
|
74
|
+
trackThreadsExampleViewed,
|
|
61
75
|
trackThreadsIntelligenceSignupClicked,
|
|
62
76
|
trackThreadsLockedViewed,
|
|
77
|
+
trackMemoriesTabClicked,
|
|
63
78
|
trackThreadsTabClicked,
|
|
64
79
|
trackThreadsTalkToEngineerClicked,
|
|
65
80
|
} from "./lib/telemetry.js";
|
|
66
|
-
import type {
|
|
81
|
+
import type {
|
|
82
|
+
InspectorMemoryTelemetryProps,
|
|
83
|
+
InspectorThreadTelemetryProps,
|
|
84
|
+
} from "./lib/telemetry.js";
|
|
67
85
|
|
|
68
86
|
export type { Anchor } from "./lib/types.js";
|
|
69
87
|
|
|
@@ -78,6 +96,7 @@ type MenuKey =
|
|
|
78
96
|
| "frontend-tools"
|
|
79
97
|
| "agent-context"
|
|
80
98
|
| "threads"
|
|
99
|
+
| "memories"
|
|
81
100
|
| "settings";
|
|
82
101
|
|
|
83
102
|
type MenuItem = {
|
|
@@ -100,7 +119,17 @@ const DOCKED_LEFT_WIDTH = 500; // Sensible width for left dock with collapsed si
|
|
|
100
119
|
const MAX_AGENT_EVENTS = 200;
|
|
101
120
|
const MAX_TOTAL_EVENTS = 500;
|
|
102
121
|
const INTELLIGENCE_SIGNUP_URL = "https://go.copilotkit.ai/intelligence-signup";
|
|
122
|
+
const THREADS_INTELLIGENCE_SIGNIN_URL =
|
|
123
|
+
"https://dashboard.operations.copilotkit.ai/sign-in";
|
|
103
124
|
const TALK_TO_ENGINEER_URL = "https://www.copilotkit.ai/talk-to-an-engineer";
|
|
125
|
+
const THREADS_DOCS_URL = "https://docs.copilotkit.ai/threads";
|
|
126
|
+
const SELF_HOSTED_INTELLIGENCE_URL =
|
|
127
|
+
"https://docs.copilotkit.ai/premium/self-hosting";
|
|
128
|
+
const THREADS_EXAMPLE_OVERVIEW_VIDEO_URL =
|
|
129
|
+
"https://cdn.copilotkit.ai/corp-site/videos/copilotkit-generative-ui-agentic-frontend-demo.webm";
|
|
130
|
+
const THREADS_EXAMPLE_TOUR_STORAGE_KEY =
|
|
131
|
+
"cpk:inspector:threads-example-tour:v1";
|
|
132
|
+
const THREADS_EXAMPLE_AGENT_ID = "threads-feature";
|
|
104
133
|
|
|
105
134
|
type ThreadServiceStatus = "available" | "unavailable" | "unknown" | "error";
|
|
106
135
|
|
|
@@ -364,6 +393,235 @@ type RuntimeStateFetchResult =
|
|
|
364
393
|
| { status: "available"; state: Record<string, unknown> | null }
|
|
365
394
|
| { status: "not-available" };
|
|
366
395
|
|
|
396
|
+
type ExampleThread = ɵThread & { isExample: true };
|
|
397
|
+
|
|
398
|
+
type ExampleThreadDetails = {
|
|
399
|
+
messages: ThreadDebuggerMessage[];
|
|
400
|
+
events: ThreadDebuggerEvent[];
|
|
401
|
+
state: Record<string, unknown>;
|
|
402
|
+
};
|
|
403
|
+
|
|
404
|
+
const THREADS_EXAMPLE_THREADS: ExampleThread[] = [
|
|
405
|
+
{
|
|
406
|
+
id: "example-realtime-sync",
|
|
407
|
+
name: "Realtime thread sync",
|
|
408
|
+
agentId: THREADS_EXAMPLE_AGENT_ID,
|
|
409
|
+
organizationId: "example-organization",
|
|
410
|
+
createdById: "example-user",
|
|
411
|
+
archived: false,
|
|
412
|
+
createdAt: "2026-07-08T16:00:00.000Z",
|
|
413
|
+
updatedAt: "2026-07-08T16:30:00.000Z",
|
|
414
|
+
isExample: true,
|
|
415
|
+
},
|
|
416
|
+
{
|
|
417
|
+
id: "example-manage-history",
|
|
418
|
+
name: "Manage saved conversations",
|
|
419
|
+
agentId: THREADS_EXAMPLE_AGENT_ID,
|
|
420
|
+
organizationId: "example-organization",
|
|
421
|
+
createdById: "example-user",
|
|
422
|
+
archived: false,
|
|
423
|
+
createdAt: "2026-07-07T17:45:00.000Z",
|
|
424
|
+
updatedAt: "2026-07-07T18:15:00.000Z",
|
|
425
|
+
isExample: true,
|
|
426
|
+
},
|
|
427
|
+
{
|
|
428
|
+
id: "example-inspect-runs",
|
|
429
|
+
name: "Inspect durable run history",
|
|
430
|
+
agentId: THREADS_EXAMPLE_AGENT_ID,
|
|
431
|
+
organizationId: "example-organization",
|
|
432
|
+
createdById: "example-user",
|
|
433
|
+
archived: false,
|
|
434
|
+
createdAt: "2026-07-06T20:15:00.000Z",
|
|
435
|
+
updatedAt: "2026-07-06T20:45:00.000Z",
|
|
436
|
+
isExample: true,
|
|
437
|
+
},
|
|
438
|
+
];
|
|
439
|
+
|
|
440
|
+
const THREADS_EXAMPLE_DETAILS: Record<string, ExampleThreadDetails> = {
|
|
441
|
+
"example-realtime-sync": {
|
|
442
|
+
messages: [
|
|
443
|
+
{
|
|
444
|
+
id: "example-sync-user",
|
|
445
|
+
role: "user",
|
|
446
|
+
content: "Resume the checkout support thread from yesterday.",
|
|
447
|
+
},
|
|
448
|
+
{
|
|
449
|
+
id: "example-sync-assistant",
|
|
450
|
+
role: "assistant",
|
|
451
|
+
content:
|
|
452
|
+
"I found the saved thread, restored the cart state, and continued from the latest user message.",
|
|
453
|
+
},
|
|
454
|
+
],
|
|
455
|
+
events: [
|
|
456
|
+
{
|
|
457
|
+
type: "RUN_STARTED",
|
|
458
|
+
timestamp: "2026-07-08T16:30:00.000Z",
|
|
459
|
+
payload: {
|
|
460
|
+
threadId: "example-realtime-sync",
|
|
461
|
+
agentId: THREADS_EXAMPLE_AGENT_ID,
|
|
462
|
+
},
|
|
463
|
+
},
|
|
464
|
+
{
|
|
465
|
+
type: "MESSAGES_SNAPSHOT",
|
|
466
|
+
timestamp: "2026-07-08T16:30:01.000Z",
|
|
467
|
+
payload: {
|
|
468
|
+
messageCount: 6,
|
|
469
|
+
source: "thread-history",
|
|
470
|
+
},
|
|
471
|
+
},
|
|
472
|
+
{
|
|
473
|
+
type: "STATE_SNAPSHOT",
|
|
474
|
+
timestamp: "2026-07-08T16:30:02.000Z",
|
|
475
|
+
payload: {
|
|
476
|
+
cartId: "cart_demo_42",
|
|
477
|
+
checkoutStep: "shipping",
|
|
478
|
+
resumed: true,
|
|
479
|
+
},
|
|
480
|
+
},
|
|
481
|
+
{
|
|
482
|
+
type: "RUN_FINISHED",
|
|
483
|
+
timestamp: "2026-07-08T16:30:04.000Z",
|
|
484
|
+
payload: {
|
|
485
|
+
status: "completed",
|
|
486
|
+
},
|
|
487
|
+
},
|
|
488
|
+
],
|
|
489
|
+
state: {
|
|
490
|
+
cartId: "cart_demo_42",
|
|
491
|
+
checkoutStep: "shipping",
|
|
492
|
+
userIntent: "resume_previous_checkout",
|
|
493
|
+
persistedThread: true,
|
|
494
|
+
},
|
|
495
|
+
},
|
|
496
|
+
"example-manage-history": {
|
|
497
|
+
messages: [
|
|
498
|
+
{
|
|
499
|
+
id: "example-history-user",
|
|
500
|
+
role: "user",
|
|
501
|
+
content: "Rename this saved support conversation for the handoff.",
|
|
502
|
+
},
|
|
503
|
+
{
|
|
504
|
+
id: "example-history-assistant",
|
|
505
|
+
role: "assistant",
|
|
506
|
+
content:
|
|
507
|
+
"Renamed the thread and kept the prior messages available for the next session.",
|
|
508
|
+
},
|
|
509
|
+
],
|
|
510
|
+
events: [
|
|
511
|
+
{
|
|
512
|
+
type: "RUN_STARTED",
|
|
513
|
+
timestamp: "2026-07-07T18:15:00.000Z",
|
|
514
|
+
payload: {
|
|
515
|
+
threadId: "example-manage-history",
|
|
516
|
+
agentId: THREADS_EXAMPLE_AGENT_ID,
|
|
517
|
+
},
|
|
518
|
+
},
|
|
519
|
+
{
|
|
520
|
+
type: "CUSTOM_EVENT",
|
|
521
|
+
timestamp: "2026-07-07T18:15:01.000Z",
|
|
522
|
+
payload: {
|
|
523
|
+
action: "thread_renamed",
|
|
524
|
+
previousName: "Untitled",
|
|
525
|
+
name: "Billing escalation handoff",
|
|
526
|
+
},
|
|
527
|
+
},
|
|
528
|
+
{
|
|
529
|
+
type: "RUN_FINISHED",
|
|
530
|
+
timestamp: "2026-07-07T18:15:03.000Z",
|
|
531
|
+
payload: {
|
|
532
|
+
status: "completed",
|
|
533
|
+
},
|
|
534
|
+
},
|
|
535
|
+
],
|
|
536
|
+
state: {
|
|
537
|
+
name: "Billing escalation handoff",
|
|
538
|
+
savedMessages: 14,
|
|
539
|
+
lastHandoff: "support-team",
|
|
540
|
+
},
|
|
541
|
+
},
|
|
542
|
+
"example-inspect-runs": {
|
|
543
|
+
messages: [
|
|
544
|
+
{
|
|
545
|
+
id: "example-inspect-user",
|
|
546
|
+
role: "user",
|
|
547
|
+
content: "Why did the assistant recommend the enterprise plan?",
|
|
548
|
+
},
|
|
549
|
+
{
|
|
550
|
+
id: "example-inspect-assistant",
|
|
551
|
+
role: "assistant",
|
|
552
|
+
content:
|
|
553
|
+
"The recommendation came from the account size, SSO requirement, and audit-log constraint in state.",
|
|
554
|
+
},
|
|
555
|
+
],
|
|
556
|
+
events: [
|
|
557
|
+
{
|
|
558
|
+
type: "RUN_STARTED",
|
|
559
|
+
timestamp: "2026-07-06T20:45:00.000Z",
|
|
560
|
+
payload: {
|
|
561
|
+
threadId: "example-inspect-runs",
|
|
562
|
+
agentId: THREADS_EXAMPLE_AGENT_ID,
|
|
563
|
+
},
|
|
564
|
+
},
|
|
565
|
+
{
|
|
566
|
+
type: "TOOL_CALL_START",
|
|
567
|
+
timestamp: "2026-07-06T20:45:01.000Z",
|
|
568
|
+
payload: {
|
|
569
|
+
toolCallId: "call_account_lookup",
|
|
570
|
+
toolName: "lookupAccount",
|
|
571
|
+
},
|
|
572
|
+
},
|
|
573
|
+
{
|
|
574
|
+
type: "TOOL_CALL_RESULT",
|
|
575
|
+
timestamp: "2026-07-06T20:45:02.000Z",
|
|
576
|
+
payload: {
|
|
577
|
+
toolCallId: "call_account_lookup",
|
|
578
|
+
seats: 220,
|
|
579
|
+
requiresSso: true,
|
|
580
|
+
},
|
|
581
|
+
},
|
|
582
|
+
{
|
|
583
|
+
type: "RUN_FINISHED",
|
|
584
|
+
timestamp: "2026-07-06T20:45:04.000Z",
|
|
585
|
+
payload: {
|
|
586
|
+
status: "completed",
|
|
587
|
+
},
|
|
588
|
+
},
|
|
589
|
+
],
|
|
590
|
+
state: {
|
|
591
|
+
accountTier: "growth",
|
|
592
|
+
seats: 220,
|
|
593
|
+
requiresSso: true,
|
|
594
|
+
auditLogsRequired: true,
|
|
595
|
+
},
|
|
596
|
+
},
|
|
597
|
+
};
|
|
598
|
+
|
|
599
|
+
const THREADS_EXAMPLE_TOUR_STEPS: ReadonlyArray<{
|
|
600
|
+
tab: ThreadDetailsTab;
|
|
601
|
+
label: string;
|
|
602
|
+
title: string;
|
|
603
|
+
body: string;
|
|
604
|
+
}> = [
|
|
605
|
+
{
|
|
606
|
+
tab: "timeline",
|
|
607
|
+
label: "Timeline",
|
|
608
|
+
title: "Read the run as a story",
|
|
609
|
+
body: "The timeline turns messages, tool calls, state changes, and run markers into a scannable debugging trail.",
|
|
610
|
+
},
|
|
611
|
+
{
|
|
612
|
+
tab: "raw-events",
|
|
613
|
+
label: "Raw AG-UI Events",
|
|
614
|
+
title: "Drop into the protocol payloads",
|
|
615
|
+
body: "Raw events show the exact AG-UI stream behind the timeline when you need to verify ordering or payload shape.",
|
|
616
|
+
},
|
|
617
|
+
{
|
|
618
|
+
tab: "state",
|
|
619
|
+
label: "State",
|
|
620
|
+
title: "Check the durable state",
|
|
621
|
+
body: "The state tab shows the saved values that make a thread resumable across sessions.",
|
|
622
|
+
},
|
|
623
|
+
];
|
|
624
|
+
|
|
367
625
|
// ─── JSON syntax highlighter ─────────────────────────────────────────────────
|
|
368
626
|
// Inline-styled so shadow DOM encapsulation preserves colors when the output
|
|
369
627
|
// is injected via unsafeHTML. Only for structured data — never raw user HTML.
|
|
@@ -590,6 +848,11 @@ class CpkThreadList extends LitElement {
|
|
|
590
848
|
color: #57575b;
|
|
591
849
|
}
|
|
592
850
|
|
|
851
|
+
.cpk-tl__pill--example {
|
|
852
|
+
background: rgba(133, 236, 206, 0.22);
|
|
853
|
+
color: #189370;
|
|
854
|
+
}
|
|
855
|
+
|
|
593
856
|
/* ── Empty state ── */
|
|
594
857
|
.cpk-tl__empty {
|
|
595
858
|
padding: 32px 16px;
|
|
@@ -685,6 +948,13 @@ class CpkThreadList extends LitElement {
|
|
|
685
948
|
</div>
|
|
686
949
|
<div class="cpk-tl__meta">
|
|
687
950
|
<span class="cpk-tl__pill">${thread.agentId}</span>
|
|
951
|
+
${
|
|
952
|
+
(thread as Partial<ExampleThread>).isExample
|
|
953
|
+
? html`
|
|
954
|
+
<span class="cpk-tl__pill cpk-tl__pill--example">Example</span>
|
|
955
|
+
`
|
|
956
|
+
: nothing
|
|
957
|
+
}
|
|
688
958
|
</div>
|
|
689
959
|
</div>
|
|
690
960
|
`,
|
|
@@ -961,6 +1231,10 @@ export class CpkThreadInspector extends LitElement {
|
|
|
961
1231
|
this.maybeFetchTabData(id);
|
|
962
1232
|
}
|
|
963
1233
|
|
|
1234
|
+
selectTab(id: ThreadDetailsTab): void {
|
|
1235
|
+
this.activateTab(id);
|
|
1236
|
+
}
|
|
1237
|
+
|
|
964
1238
|
private maybeFetchTabData(id: ThreadDetailsTab): void {
|
|
965
1239
|
// Lazy-trigger the events / state fetches so their (potentially huge)
|
|
966
1240
|
// JSON.parse only blocks the main thread after the user has shown
|
|
@@ -3418,166 +3692,563 @@ ${unsafeHTML(highlightedJson(stateValue))}</pre
|
|
|
3418
3692
|
}
|
|
3419
3693
|
}
|
|
3420
3694
|
|
|
3421
|
-
//
|
|
3422
|
-
// Inspector shell. Keep this class thin so the public body remains the single
|
|
3423
|
-
// implementation.
|
|
3424
|
-
export class ɵCpkThreadDetails extends CpkThreadInspector {}
|
|
3695
|
+
// ─── cpk-memory-list ─────────────────────────────────────────────────────────
|
|
3425
3696
|
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
|
-
}
|
|
3429
|
-
if (!customElements.get(THREAD_INSPECTOR_TAG)) {
|
|
3430
|
-
customElements.define(THREAD_INSPECTOR_TAG, CpkThreadInspector);
|
|
3431
|
-
}
|
|
3432
|
-
if (!customElements.get("cpk-thread-details")) {
|
|
3433
|
-
customElements.define("cpk-thread-details", ɵCpkThreadDetails);
|
|
3434
|
-
}
|
|
3697
|
+
/** Memory kind values including the "all" sentinel used by the filter UI. */
|
|
3698
|
+
type MemoryKindFilter = "all" | "topical" | "episodic" | "operational";
|
|
3435
3699
|
|
|
3436
|
-
|
|
3700
|
+
class CpkMemoryList extends LitElement {
|
|
3437
3701
|
static properties = {
|
|
3438
|
-
|
|
3439
|
-
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
private _core: CopilotKitCore | null = null;
|
|
3443
|
-
private coreSubscriber: CopilotKitCoreSubscriber | null = null;
|
|
3444
|
-
private coreUnsubscribe: (() => void) | null = null;
|
|
3445
|
-
private runtimeStatus: CopilotKitCoreRuntimeConnectionStatus | null = null;
|
|
3446
|
-
private coreProperties: Readonly<Record<string, unknown>> = {};
|
|
3447
|
-
private lastCoreError: {
|
|
3448
|
-
code: CopilotKitCoreErrorCode;
|
|
3449
|
-
message: string;
|
|
3450
|
-
} | null = null;
|
|
3451
|
-
private agentSubscriptions: Map<string, () => void> = new Map();
|
|
3452
|
-
private agentEvents: Map<string, InspectorEvent[]> = new Map();
|
|
3453
|
-
private agentMessages: Map<string, InspectorMessage[]> = new Map();
|
|
3454
|
-
// Per-thread monotonic version that ticks every time an agent currently
|
|
3455
|
-
// running on that thread emits a message change. `cpk-thread-details`
|
|
3456
|
-
// watches this prop and re-fetches `/threads/:id/messages` when it changes,
|
|
3457
|
-
// which is how live updates flow into the conversation view without
|
|
3458
|
-
// duplicating the runtime's message-shape conversion in the inspector.
|
|
3459
|
-
private liveMessageVersion: Map<string, number> = new Map();
|
|
3460
|
-
private agentStates: Map<string, SanitizedValue> = new Map();
|
|
3461
|
-
private flattenedEvents: InspectorEvent[] = [];
|
|
3462
|
-
private eventCounter = 0;
|
|
3463
|
-
private contextStore: Record<
|
|
3464
|
-
string,
|
|
3465
|
-
{ description?: string; value: unknown }
|
|
3466
|
-
> = {};
|
|
3702
|
+
memories: { attribute: false },
|
|
3703
|
+
search: { state: true },
|
|
3704
|
+
kind: { state: true },
|
|
3705
|
+
};
|
|
3467
3706
|
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
private
|
|
3471
|
-
private
|
|
3472
|
-
private pointerContext: ContextKey | null = null;
|
|
3473
|
-
private isOpen = false;
|
|
3474
|
-
private draggedDuringInteraction = false;
|
|
3475
|
-
private ignoreNextButtonClick = false;
|
|
3476
|
-
private selectedMenu: MenuKey = "ag-ui-events";
|
|
3477
|
-
private selectedThreadId: string | null = null;
|
|
3478
|
-
private threadListWidth = 290;
|
|
3479
|
-
private threadDividerResizing = false;
|
|
3480
|
-
private threadDividerPointerId = -1;
|
|
3481
|
-
private threadDividerStartX = 0;
|
|
3482
|
-
private threadDividerStartWidth = 0;
|
|
3483
|
-
private _threads: ɵThread[] = [];
|
|
3484
|
-
private _threadStoreSubscriptions: Map<string, () => void> = new Map();
|
|
3485
|
-
private _threadsByAgent: Map<string, ɵThread[]> = new Map();
|
|
3486
|
-
// Error from each agent's thread store (REST list rejection, Phoenix
|
|
3487
|
-
// subscribe failure, retry exhaustion). When non-empty for the active
|
|
3488
|
-
// selection, the threads view renders an error state instead of stale
|
|
3489
|
-
// data with no indication.
|
|
3490
|
-
private _threadsErrorByAgent: Map<string, Error> = new Map();
|
|
3491
|
-
// Thread stores created and owned by the inspector (keyed by agentId)
|
|
3492
|
-
private _ownedThreadStores: Map<string, ɵThreadStore> = new Map();
|
|
3493
|
-
private contextMenuOpen = false;
|
|
3494
|
-
private dockMode: DockMode = "floating";
|
|
3495
|
-
private previousBodyMargins: { left: string; bottom: string } | null = null;
|
|
3496
|
-
private transitionTimeoutId: ReturnType<typeof setTimeout> | null = null;
|
|
3497
|
-
private bodyTransitionTimeoutIds: Set<ReturnType<typeof setTimeout>> =
|
|
3498
|
-
new Set();
|
|
3499
|
-
private pendingSelectedContext: string | null = null;
|
|
3500
|
-
private autoAttachCore = true;
|
|
3501
|
-
private attemptedAutoAttach = false;
|
|
3502
|
-
private cachedTools: InspectorToolDefinition[] = [];
|
|
3503
|
-
private toolSignature = "";
|
|
3504
|
-
private eventFilterText = "";
|
|
3505
|
-
private eventTypeFilter: InspectorAgentEventType | "all" = "all";
|
|
3506
|
-
// Column widths for the AG-UI events table (agent, time, event-type; last col is auto)
|
|
3507
|
-
private evtColWidths = [100, 80, 150];
|
|
3508
|
-
private _evtColResize: {
|
|
3509
|
-
col: number;
|
|
3510
|
-
startX: number;
|
|
3511
|
-
startW: number;
|
|
3512
|
-
} | null = null;
|
|
3707
|
+
/** Ordered (newest-first) list of memories supplied by the parent. */
|
|
3708
|
+
memories: Memory[] = [];
|
|
3709
|
+
private search = "";
|
|
3710
|
+
private kind: MemoryKindFilter = "all";
|
|
3513
3711
|
|
|
3514
|
-
|
|
3515
|
-
|
|
3516
|
-
private announcementPreviewText: string | null = null;
|
|
3517
|
-
// Forward-compat for an optional `cta_label` field on the announcement
|
|
3518
|
-
// CDN payload (e.g. "Try threads", "New feature"). The current schema
|
|
3519
|
-
// ({timestamp, previewText, announcement}) doesn't carry it, so this is
|
|
3520
|
-
// null in production today; we read it defensively in fetchAnnouncement
|
|
3521
|
-
// so a future CDN-side schema bump lights up `cta_label` on banner_clicked
|
|
3522
|
-
// without an inspector release.
|
|
3523
|
-
private announcementCtaLabel: string | null = null;
|
|
3524
|
-
private hasUnseenAnnouncement = false;
|
|
3525
|
-
private announcementLoaded = false;
|
|
3526
|
-
private announcementPromise: Promise<void> | null = null;
|
|
3527
|
-
private showAnnouncementPreview = true;
|
|
3528
|
-
private announcementExpanded = false;
|
|
3529
|
-
// Per-instance dedup for `oss.inspector.banner_viewed` so the event fires
|
|
3530
|
-
// at most once per announcement timestamp per inspector mount. Plan calls
|
|
3531
|
-
// for "de-dup per timestamp per session"; instance-scoping is closer
|
|
3532
|
-
// to per-mount than per-tab (sessionStorage), but for the inspector the
|
|
3533
|
-
// distinction is academic — inspector instances rarely outlive the page.
|
|
3534
|
-
private viewedBannerTimestamps: Set<string> = new Set();
|
|
3535
|
-
private pendingBannerViewed: {
|
|
3536
|
-
banner_id: string;
|
|
3537
|
-
cta_label?: string;
|
|
3538
|
-
} | null = null;
|
|
3539
|
-
// Per-instance dedup for `oss.inspector.banner_clicked` (keyed by
|
|
3540
|
-
// `${bannerId}:${cta}`) so copy-button retries and accidental multi-clicks
|
|
3541
|
-
// don't inflate funnel counts beyond one signal per intent type per banner.
|
|
3542
|
-
private clickedBannerIds: Set<string> = new Set();
|
|
3543
|
-
private viewedThreadsTelemetryStates: Set<string> = new Set();
|
|
3712
|
+
static styles = css`
|
|
3713
|
+
@import url("https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600&family=Spline+Sans+Mono:wght@400;500&display=swap");
|
|
3544
3714
|
|
|
3545
|
-
|
|
3546
|
-
|
|
3547
|
-
|
|
3715
|
+
:host {
|
|
3716
|
+
display: flex;
|
|
3717
|
+
flex-direction: column;
|
|
3718
|
+
height: 100%;
|
|
3719
|
+
overflow: hidden;
|
|
3720
|
+
}
|
|
3548
3721
|
|
|
3549
|
-
|
|
3550
|
-
|
|
3551
|
-
|
|
3552
|
-
|
|
3722
|
+
.cpk-ml {
|
|
3723
|
+
font-family: "Plus Jakarta Sans", sans-serif;
|
|
3724
|
+
display: flex;
|
|
3725
|
+
flex-direction: column;
|
|
3726
|
+
height: 100%;
|
|
3727
|
+
overflow: hidden;
|
|
3728
|
+
background: #f7f7f9;
|
|
3553
3729
|
}
|
|
3554
3730
|
|
|
3555
|
-
|
|
3731
|
+
/* ── Search ── */
|
|
3732
|
+
.cpk-ml__search {
|
|
3733
|
+
padding: 10px 12px;
|
|
3734
|
+
border-bottom: 1px solid #dbdbe5;
|
|
3735
|
+
flex-shrink: 0;
|
|
3736
|
+
}
|
|
3556
3737
|
|
|
3557
|
-
|
|
3558
|
-
|
|
3738
|
+
.cpk-ml__search-input {
|
|
3739
|
+
width: 100%;
|
|
3740
|
+
box-sizing: border-box;
|
|
3741
|
+
font-family: "Plus Jakarta Sans", sans-serif;
|
|
3742
|
+
font-size: 12px;
|
|
3743
|
+
padding: 7px 10px;
|
|
3744
|
+
border-radius: 6px;
|
|
3745
|
+
border: 1px solid #dbdbe5;
|
|
3746
|
+
background: #ffffff;
|
|
3747
|
+
color: #010507;
|
|
3748
|
+
outline: none;
|
|
3749
|
+
transition: border-color 0.15s;
|
|
3750
|
+
}
|
|
3559
3751
|
|
|
3560
|
-
|
|
3561
|
-
|
|
3752
|
+
.cpk-ml__search-input:focus {
|
|
3753
|
+
border-color: #bec2ff;
|
|
3562
3754
|
}
|
|
3563
|
-
}
|
|
3564
3755
|
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
|
|
3572
|
-
|
|
3573
|
-
|
|
3574
|
-
size: { ...DEFAULT_WINDOW_SIZE },
|
|
3575
|
-
anchor: { horizontal: "right", vertical: "top" },
|
|
3576
|
-
anchorOffset: { x: EDGE_MARGIN, y: EDGE_MARGIN },
|
|
3577
|
-
},
|
|
3578
|
-
};
|
|
3756
|
+
/* ── Kind filter ── */
|
|
3757
|
+
.cpk-ml__filter {
|
|
3758
|
+
display: flex;
|
|
3759
|
+
gap: 4px;
|
|
3760
|
+
padding: 8px 12px;
|
|
3761
|
+
border-bottom: 1px solid #dbdbe5;
|
|
3762
|
+
flex-shrink: 0;
|
|
3763
|
+
flex-wrap: wrap;
|
|
3764
|
+
}
|
|
3579
3765
|
|
|
3580
|
-
|
|
3766
|
+
.cpk-ml__filter-seg {
|
|
3767
|
+
font-family: "Plus Jakarta Sans", sans-serif;
|
|
3768
|
+
font-size: 11px;
|
|
3769
|
+
font-weight: 500;
|
|
3770
|
+
padding: 3px 9px;
|
|
3771
|
+
border-radius: 5px;
|
|
3772
|
+
border: 1px solid #dbdbe5;
|
|
3773
|
+
background: #ffffff;
|
|
3774
|
+
color: #57575b;
|
|
3775
|
+
cursor: pointer;
|
|
3776
|
+
transition:
|
|
3777
|
+
background 0.1s,
|
|
3778
|
+
border-color 0.1s,
|
|
3779
|
+
color 0.1s;
|
|
3780
|
+
user-select: none;
|
|
3781
|
+
}
|
|
3782
|
+
|
|
3783
|
+
.cpk-ml__filter-seg:hover {
|
|
3784
|
+
background: #f0f0f5;
|
|
3785
|
+
}
|
|
3786
|
+
|
|
3787
|
+
.cpk-ml__filter-seg--active {
|
|
3788
|
+
background: #bec2ff1a;
|
|
3789
|
+
border-color: #bec2ff;
|
|
3790
|
+
color: #010507;
|
|
3791
|
+
}
|
|
3792
|
+
|
|
3793
|
+
.cpk-ml__filter-count {
|
|
3794
|
+
font-family: "Spline Sans Mono", monospace;
|
|
3795
|
+
font-size: 9px;
|
|
3796
|
+
margin-left: 4px;
|
|
3797
|
+
color: #838389;
|
|
3798
|
+
}
|
|
3799
|
+
|
|
3800
|
+
/* ── List ── */
|
|
3801
|
+
.cpk-ml__list {
|
|
3802
|
+
flex: 1;
|
|
3803
|
+
overflow-y: auto;
|
|
3804
|
+
padding: 8px 12px;
|
|
3805
|
+
display: flex;
|
|
3806
|
+
flex-direction: column;
|
|
3807
|
+
gap: 8px;
|
|
3808
|
+
}
|
|
3809
|
+
|
|
3810
|
+
/* ── Card ── */
|
|
3811
|
+
.cpk-ml__card {
|
|
3812
|
+
background: #ffffff;
|
|
3813
|
+
border: 1px solid #e9e9ef;
|
|
3814
|
+
border-radius: 8px;
|
|
3815
|
+
padding: 10px 12px;
|
|
3816
|
+
display: flex;
|
|
3817
|
+
flex-direction: column;
|
|
3818
|
+
gap: 6px;
|
|
3819
|
+
}
|
|
3820
|
+
|
|
3821
|
+
.cpk-ml__card-badges {
|
|
3822
|
+
display: flex;
|
|
3823
|
+
gap: 6px;
|
|
3824
|
+
align-items: center;
|
|
3825
|
+
flex-wrap: wrap;
|
|
3826
|
+
}
|
|
3827
|
+
|
|
3828
|
+
/* Kind badge — color per kind */
|
|
3829
|
+
.cpk-ml__kind-badge {
|
|
3830
|
+
font-family: "Spline Sans Mono", monospace;
|
|
3831
|
+
font-size: 9px;
|
|
3832
|
+
padding: 1px 7px;
|
|
3833
|
+
border-radius: 4px;
|
|
3834
|
+
text-transform: uppercase;
|
|
3835
|
+
font-weight: 500;
|
|
3836
|
+
white-space: nowrap;
|
|
3837
|
+
}
|
|
3838
|
+
|
|
3839
|
+
.cpk-ml__kind-badge--topical {
|
|
3840
|
+
background: #eee6fe;
|
|
3841
|
+
color: #57575b;
|
|
3842
|
+
}
|
|
3843
|
+
|
|
3844
|
+
.cpk-ml__kind-badge--episodic {
|
|
3845
|
+
background: #e6f4fe;
|
|
3846
|
+
color: #2d5f80;
|
|
3847
|
+
}
|
|
3848
|
+
|
|
3849
|
+
.cpk-ml__kind-badge--operational {
|
|
3850
|
+
background: #e6feee;
|
|
3851
|
+
color: #2d6645;
|
|
3852
|
+
}
|
|
3853
|
+
|
|
3854
|
+
/* Scope badge */
|
|
3855
|
+
.cpk-ml__scope-badge {
|
|
3856
|
+
font-family: "Spline Sans Mono", monospace;
|
|
3857
|
+
font-size: 9px;
|
|
3858
|
+
padding: 1px 7px;
|
|
3859
|
+
border-radius: 4px;
|
|
3860
|
+
text-transform: uppercase;
|
|
3861
|
+
font-weight: 500;
|
|
3862
|
+
white-space: nowrap;
|
|
3863
|
+
background: #f0f0f5;
|
|
3864
|
+
color: #838389;
|
|
3865
|
+
}
|
|
3866
|
+
|
|
3867
|
+
/* Content */
|
|
3868
|
+
.cpk-ml__content {
|
|
3869
|
+
font-size: 12px;
|
|
3870
|
+
color: #010507;
|
|
3871
|
+
line-height: 1.5;
|
|
3872
|
+
word-break: break-word;
|
|
3873
|
+
}
|
|
3874
|
+
|
|
3875
|
+
/* Footer */
|
|
3876
|
+
.cpk-ml__footer {
|
|
3877
|
+
display: flex;
|
|
3878
|
+
align-items: center;
|
|
3879
|
+
justify-content: space-between;
|
|
3880
|
+
gap: 8px;
|
|
3881
|
+
margin-top: 2px;
|
|
3882
|
+
}
|
|
3883
|
+
|
|
3884
|
+
.cpk-ml__footer-threads {
|
|
3885
|
+
font-size: 10px;
|
|
3886
|
+
color: #838389;
|
|
3887
|
+
}
|
|
3888
|
+
|
|
3889
|
+
.cpk-ml__footer-id {
|
|
3890
|
+
font-family: "Spline Sans Mono", monospace;
|
|
3891
|
+
font-size: 9px;
|
|
3892
|
+
color: #c0c0c8;
|
|
3893
|
+
}
|
|
3894
|
+
|
|
3895
|
+
/* ── Empty state ── */
|
|
3896
|
+
.cpk-ml__empty {
|
|
3897
|
+
padding: 32px 16px;
|
|
3898
|
+
text-align: center;
|
|
3899
|
+
color: #838389;
|
|
3900
|
+
font-size: 12px;
|
|
3901
|
+
display: flex;
|
|
3902
|
+
flex-direction: column;
|
|
3903
|
+
align-items: center;
|
|
3904
|
+
gap: 8px;
|
|
3905
|
+
}
|
|
3906
|
+
|
|
3907
|
+
.cpk-ml__empty-icon {
|
|
3908
|
+
color: #c0c0c8;
|
|
3909
|
+
}
|
|
3910
|
+
`;
|
|
3911
|
+
|
|
3912
|
+
/** Memories that pass the current text search (before kind filter). */
|
|
3913
|
+
private get searchFiltered(): Memory[] {
|
|
3914
|
+
const q = this.search.trim().toLowerCase();
|
|
3915
|
+
if (!q) return this.memories;
|
|
3916
|
+
return this.memories.filter((m) => m.content.toLowerCase().includes(q));
|
|
3917
|
+
}
|
|
3918
|
+
|
|
3919
|
+
/** Memories that pass both search and kind filter. */
|
|
3920
|
+
private get filtered(): Memory[] {
|
|
3921
|
+
const searched = this.searchFiltered;
|
|
3922
|
+
if (this.kind === "all") return searched;
|
|
3923
|
+
return searched.filter((m) => m.kind === this.kind);
|
|
3924
|
+
}
|
|
3925
|
+
|
|
3926
|
+
/** Count of search-filtered memories for a given kind (for segment labels). */
|
|
3927
|
+
private countForKind(kind: Exclude<MemoryKindFilter, "all">): number {
|
|
3928
|
+
return this.searchFiltered.filter((m) => m.kind === kind).length;
|
|
3929
|
+
}
|
|
3930
|
+
|
|
3931
|
+
private onSearchInput = (event: Event): void => {
|
|
3932
|
+
this.search = (event.target as HTMLInputElement).value;
|
|
3933
|
+
};
|
|
3934
|
+
|
|
3935
|
+
private onKindClick = (event: Event): void => {
|
|
3936
|
+
const seg = (event.target as HTMLElement).closest("[data-kind]");
|
|
3937
|
+
if (!seg) return;
|
|
3938
|
+
const k = (seg as HTMLElement).dataset["kind"] as MemoryKindFilter;
|
|
3939
|
+
this.kind = k;
|
|
3940
|
+
};
|
|
3941
|
+
|
|
3942
|
+
/** Truncate an id to first-4…last-4 characters. */
|
|
3943
|
+
private shortId(id: string): string {
|
|
3944
|
+
if (id.length <= 12) return id;
|
|
3945
|
+
return `${id.slice(0, 4)}…${id.slice(-4)}`;
|
|
3946
|
+
}
|
|
3947
|
+
|
|
3948
|
+
private renderKindBadge(kind: string): TemplateResult {
|
|
3949
|
+
return html`<span class="cpk-ml__kind-badge cpk-ml__kind-badge--${kind}"
|
|
3950
|
+
>${kind}</span
|
|
3951
|
+
>`;
|
|
3952
|
+
}
|
|
3953
|
+
|
|
3954
|
+
private renderEmpty(): TemplateResult {
|
|
3955
|
+
const q = this.search.trim();
|
|
3956
|
+
if (this.memories.length === 0) {
|
|
3957
|
+
return html`
|
|
3958
|
+
<div class="cpk-ml__empty">
|
|
3959
|
+
<svg
|
|
3960
|
+
width="24"
|
|
3961
|
+
height="24"
|
|
3962
|
+
viewBox="0 0 24 24"
|
|
3963
|
+
fill="none"
|
|
3964
|
+
stroke="currentColor"
|
|
3965
|
+
stroke-width="1.5"
|
|
3966
|
+
stroke-linecap="round"
|
|
3967
|
+
stroke-linejoin="round"
|
|
3968
|
+
class="cpk-ml__empty-icon"
|
|
3969
|
+
>
|
|
3970
|
+
<ellipse cx="12" cy="5" rx="9" ry="3" />
|
|
3971
|
+
<path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3" />
|
|
3972
|
+
<path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5" />
|
|
3973
|
+
</svg>
|
|
3974
|
+
No memories yet — tell the agent a durable fact and watch it appear.
|
|
3975
|
+
</div>
|
|
3976
|
+
`;
|
|
3977
|
+
}
|
|
3978
|
+
if (q) {
|
|
3979
|
+
return html`
|
|
3980
|
+
<div class="cpk-ml__empty">
|
|
3981
|
+
No memories match “${q}”.
|
|
3982
|
+
</div>
|
|
3983
|
+
`;
|
|
3984
|
+
}
|
|
3985
|
+
return html`
|
|
3986
|
+
<div class="cpk-ml__empty">No ${this.kind} memories yet.</div>
|
|
3987
|
+
`;
|
|
3988
|
+
}
|
|
3989
|
+
|
|
3990
|
+
render() {
|
|
3991
|
+
const filtered = this.filtered;
|
|
3992
|
+
const kinds: Array<Exclude<MemoryKindFilter, "all">> = [
|
|
3993
|
+
"topical",
|
|
3994
|
+
"episodic",
|
|
3995
|
+
"operational",
|
|
3996
|
+
];
|
|
3997
|
+
|
|
3998
|
+
return html`
|
|
3999
|
+
<div class="cpk-ml">
|
|
4000
|
+
<!-- Search -->
|
|
4001
|
+
<div class="cpk-ml__search">
|
|
4002
|
+
<input
|
|
4003
|
+
type="text"
|
|
4004
|
+
placeholder="Search memories…"
|
|
4005
|
+
.value=${this.search}
|
|
4006
|
+
@input=${this.onSearchInput}
|
|
4007
|
+
class="cpk-ml__search-input"
|
|
4008
|
+
/>
|
|
4009
|
+
</div>
|
|
4010
|
+
|
|
4011
|
+
<!-- Kind filter -->
|
|
4012
|
+
<div class="cpk-ml__filter" @click=${this.onKindClick}>
|
|
4013
|
+
<button
|
|
4014
|
+
class="cpk-ml__filter-seg ${this.kind === "all" ? "cpk-ml__filter-seg--active" : ""}"
|
|
4015
|
+
data-kind="all"
|
|
4016
|
+
>
|
|
4017
|
+
All<span class="cpk-ml__filter-count">${this.searchFiltered.length}</span>
|
|
4018
|
+
</button>
|
|
4019
|
+
${kinds.map(
|
|
4020
|
+
(k) => html`
|
|
4021
|
+
<button
|
|
4022
|
+
class="cpk-ml__filter-seg ${this.kind === k ? "cpk-ml__filter-seg--active" : ""}"
|
|
4023
|
+
data-kind="${k}"
|
|
4024
|
+
>
|
|
4025
|
+
${k}<span class="cpk-ml__filter-count">${this.countForKind(k)}</span>
|
|
4026
|
+
</button>
|
|
4027
|
+
`,
|
|
4028
|
+
)}
|
|
4029
|
+
</div>
|
|
4030
|
+
|
|
4031
|
+
<!-- Memory list -->
|
|
4032
|
+
<div class="cpk-ml__list">
|
|
4033
|
+
${filtered.map(
|
|
4034
|
+
(m) => html`
|
|
4035
|
+
<div class="cpk-ml__card">
|
|
4036
|
+
<div class="cpk-ml__card-badges">
|
|
4037
|
+
${this.renderKindBadge(m.kind)}
|
|
4038
|
+
<span class="cpk-ml__scope-badge">${m.scope}</span>
|
|
4039
|
+
</div>
|
|
4040
|
+
<div class="cpk-ml__content">${m.content}</div>
|
|
4041
|
+
<div class="cpk-ml__footer">
|
|
4042
|
+
<span class="cpk-ml__footer-threads"
|
|
4043
|
+
>${m.sourceThreadIds.length} source thread${m.sourceThreadIds.length === 1 ? "" : "s"}</span
|
|
4044
|
+
>
|
|
4045
|
+
<span class="cpk-ml__footer-id">${this.shortId(m.id)}</span>
|
|
4046
|
+
</div>
|
|
4047
|
+
</div>
|
|
4048
|
+
`,
|
|
4049
|
+
)}
|
|
4050
|
+
${filtered.length === 0 ? this.renderEmpty() : nothing}
|
|
4051
|
+
</div>
|
|
4052
|
+
</div>
|
|
4053
|
+
`;
|
|
4054
|
+
}
|
|
4055
|
+
}
|
|
4056
|
+
|
|
4057
|
+
// Backwards-compatible internal element name used by the full CopilotKit
|
|
4058
|
+
// Inspector shell. Keep this class thin so the public body remains the single
|
|
4059
|
+
// implementation.
|
|
4060
|
+
export class ɵCpkThreadDetails extends CpkThreadInspector {}
|
|
4061
|
+
|
|
4062
|
+
if (!customElements.get("cpk-thread-list")) {
|
|
4063
|
+
customElements.define("cpk-thread-list", CpkThreadList);
|
|
4064
|
+
}
|
|
4065
|
+
if (!customElements.get(THREAD_INSPECTOR_TAG)) {
|
|
4066
|
+
customElements.define(THREAD_INSPECTOR_TAG, CpkThreadInspector);
|
|
4067
|
+
}
|
|
4068
|
+
if (!customElements.get("cpk-thread-details")) {
|
|
4069
|
+
customElements.define("cpk-thread-details", ɵCpkThreadDetails);
|
|
4070
|
+
}
|
|
4071
|
+
if (!customElements.get("cpk-memory-list")) {
|
|
4072
|
+
customElements.define("cpk-memory-list", CpkMemoryList);
|
|
4073
|
+
}
|
|
4074
|
+
|
|
4075
|
+
export class WebInspectorElement extends LitElement {
|
|
4076
|
+
static properties = {
|
|
4077
|
+
core: { attribute: false },
|
|
4078
|
+
autoAttachCore: { type: Boolean, attribute: "auto-attach-core" },
|
|
4079
|
+
} as const;
|
|
4080
|
+
|
|
4081
|
+
private _core: CopilotKitCore | null = null;
|
|
4082
|
+
private coreSubscriber: CopilotKitCoreSubscriber | null = null;
|
|
4083
|
+
private coreUnsubscribe: (() => void) | null = null;
|
|
4084
|
+
private _memories: Memory[] = [];
|
|
4085
|
+
private _memoriesLoading = false;
|
|
4086
|
+
private _memoriesError: Error | null = null;
|
|
4087
|
+
private _memoriesAvailable = true;
|
|
4088
|
+
// Realtime-connection health, independent of `_memoriesAvailable` (the REST
|
|
4089
|
+
// list route). Drives the "live" indicator: only "connected" shows "live".
|
|
4090
|
+
private _memoriesRealtimeStatus: MemoryRealtimeStatus = "connecting";
|
|
4091
|
+
private _memoryUnsub: (() => void) | null = null;
|
|
4092
|
+
// Lazy-subscription guard. The memory store is created + started + opens
|
|
4093
|
+
// realtime the first time `core.getMemoryStore()` is called, so we defer
|
|
4094
|
+
// that call until the user actually activates the Memories tab (see
|
|
4095
|
+
// `ensureMemorySubscription`). This flag prevents a repeated tab click from
|
|
4096
|
+
// double-subscribing; `detachFromCore` resets it so a later attach + tab
|
|
4097
|
+
// activation re-subscribes cleanly.
|
|
4098
|
+
private _memorySubscribed = false;
|
|
4099
|
+
// True when the attached core predates `getMemoryStore` (older @copilotkit
|
|
4100
|
+
// SDK). Distinct from `_memoriesAvailable` (memory not enabled on an
|
|
4101
|
+
// otherwise-current deployment) so the teaser can show upgrade-the-SDK copy.
|
|
4102
|
+
private _memoryStoreUnsupported = false;
|
|
4103
|
+
private runtimeStatus: CopilotKitCoreRuntimeConnectionStatus | null = null;
|
|
4104
|
+
private coreProperties: Readonly<Record<string, unknown>> = {};
|
|
4105
|
+
private lastCoreError: {
|
|
4106
|
+
code: CopilotKitCoreErrorCode;
|
|
4107
|
+
message: string;
|
|
4108
|
+
} | null = null;
|
|
4109
|
+
private agentSubscriptions: Map<string, () => void> = new Map();
|
|
4110
|
+
private agentEvents: Map<string, InspectorEvent[]> = new Map();
|
|
4111
|
+
private agentMessages: Map<string, InspectorMessage[]> = new Map();
|
|
4112
|
+
// Per-thread monotonic version that ticks every time an agent currently
|
|
4113
|
+
// running on that thread emits a message change. `cpk-thread-details`
|
|
4114
|
+
// watches this prop and re-fetches `/threads/:id/messages` when it changes,
|
|
4115
|
+
// which is how live updates flow into the conversation view without
|
|
4116
|
+
// duplicating the runtime's message-shape conversion in the inspector.
|
|
4117
|
+
private liveMessageVersion: Map<string, number> = new Map();
|
|
4118
|
+
private agentStates: Map<string, SanitizedValue> = new Map();
|
|
4119
|
+
private flattenedEvents: InspectorEvent[] = [];
|
|
4120
|
+
private eventCounter = 0;
|
|
4121
|
+
private contextStore: Record<
|
|
4122
|
+
string,
|
|
4123
|
+
{ description?: string; value: unknown }
|
|
4124
|
+
> = {};
|
|
4125
|
+
|
|
4126
|
+
private pointerId: number | null = null;
|
|
4127
|
+
private dragStart: Position | null = null;
|
|
4128
|
+
private dragOffset: Position = { x: 0, y: 0 };
|
|
4129
|
+
private isDragging = false;
|
|
4130
|
+
private pointerContext: ContextKey | null = null;
|
|
4131
|
+
private isOpen = false;
|
|
4132
|
+
private draggedDuringInteraction = false;
|
|
4133
|
+
private ignoreNextButtonClick = false;
|
|
4134
|
+
private selectedMenu: MenuKey = "ag-ui-events";
|
|
4135
|
+
private selectedThreadId: string | null = null;
|
|
4136
|
+
private threadListWidth = 290;
|
|
4137
|
+
private threadDividerResizing = false;
|
|
4138
|
+
private threadDividerPointerId = -1;
|
|
4139
|
+
private threadDividerStartX = 0;
|
|
4140
|
+
private threadDividerStartWidth = 0;
|
|
4141
|
+
private _threads: ɵThread[] = [];
|
|
4142
|
+
private _threadStoreSubscriptions: Map<string, () => void> = new Map();
|
|
4143
|
+
private _threadsByAgent: Map<string, ɵThread[]> = new Map();
|
|
4144
|
+
// Error from each agent's thread store (REST list rejection, Phoenix
|
|
4145
|
+
// subscribe failure, retry exhaustion). When non-empty for the active
|
|
4146
|
+
// selection, the threads view renders an error state instead of stale
|
|
4147
|
+
// data with no indication.
|
|
4148
|
+
private _threadsErrorByAgent: Map<string, Error> = new Map();
|
|
4149
|
+
// Thread stores created and owned by the inspector (keyed by agentId)
|
|
4150
|
+
private _ownedThreadStores: Map<string, ɵThreadStore> = new Map();
|
|
4151
|
+
private contextMenuOpen = false;
|
|
4152
|
+
private dockMode: DockMode = "floating";
|
|
4153
|
+
private previousBodyMargins: { left: string; bottom: string } | null = null;
|
|
4154
|
+
private transitionTimeoutId: ReturnType<typeof setTimeout> | null = null;
|
|
4155
|
+
private bodyTransitionTimeoutIds: Set<ReturnType<typeof setTimeout>> =
|
|
4156
|
+
new Set();
|
|
4157
|
+
private pendingSelectedContext: string | null = null;
|
|
4158
|
+
private autoAttachCore = true;
|
|
4159
|
+
private attemptedAutoAttach = false;
|
|
4160
|
+
private cachedTools: InspectorToolDefinition[] = [];
|
|
4161
|
+
private toolSignature = "";
|
|
4162
|
+
private eventFilterText = "";
|
|
4163
|
+
private eventTypeFilter: InspectorAgentEventType | "all" = "all";
|
|
4164
|
+
// Column widths for the AG-UI events table (agent, time, event-type; last col is auto)
|
|
4165
|
+
private evtColWidths = [100, 80, 150];
|
|
4166
|
+
private _evtColResize: {
|
|
4167
|
+
col: number;
|
|
4168
|
+
startX: number;
|
|
4169
|
+
startW: number;
|
|
4170
|
+
} | null = null;
|
|
4171
|
+
|
|
4172
|
+
private announcementHtml: string | null = null;
|
|
4173
|
+
private announcementTimestamp: string | null = null;
|
|
4174
|
+
private announcementPreviewText: string | null = null;
|
|
4175
|
+
// Forward-compat for an optional `cta_label` field on the announcement
|
|
4176
|
+
// CDN payload (e.g. "Try threads", "New feature"). The current schema
|
|
4177
|
+
// ({timestamp, previewText, announcement}) doesn't carry it, so this is
|
|
4178
|
+
// null in production today; we read it defensively in fetchAnnouncement
|
|
4179
|
+
// so a future CDN-side schema bump lights up `cta_label` on banner_clicked
|
|
4180
|
+
// without an inspector release.
|
|
4181
|
+
private announcementCtaLabel: string | null = null;
|
|
4182
|
+
private hasUnseenAnnouncement = false;
|
|
4183
|
+
private announcementLoaded = false;
|
|
4184
|
+
private announcementPromise: Promise<void> | null = null;
|
|
4185
|
+
private showAnnouncementPreview = true;
|
|
4186
|
+
private announcementExpanded = false;
|
|
4187
|
+
// Per-instance dedup for `oss.inspector.banner_viewed` so the event fires
|
|
4188
|
+
// at most once per announcement timestamp per inspector mount. Plan calls
|
|
4189
|
+
// for "de-dup per timestamp per session"; instance-scoping is closer
|
|
4190
|
+
// to per-mount than per-tab (sessionStorage), but for the inspector the
|
|
4191
|
+
// distinction is academic — inspector instances rarely outlive the page.
|
|
4192
|
+
private viewedBannerTimestamps: Set<string> = new Set();
|
|
4193
|
+
private pendingBannerViewed: {
|
|
4194
|
+
banner_id: string;
|
|
4195
|
+
cta_label?: string;
|
|
4196
|
+
} | null = null;
|
|
4197
|
+
// Per-instance dedup for `oss.inspector.banner_clicked` (keyed by
|
|
4198
|
+
// `${bannerId}:${cta}`) so copy-button retries and accidental multi-clicks
|
|
4199
|
+
// don't inflate funnel counts beyond one signal per intent type per banner.
|
|
4200
|
+
private clickedBannerIds: Set<string> = new Set();
|
|
4201
|
+
private viewedThreadsTelemetryStates: Set<string> = new Set();
|
|
4202
|
+
private viewedExampleThreadIds: Set<string> = new Set();
|
|
4203
|
+
private selectedExampleThreadIds: Set<string> = new Set();
|
|
4204
|
+
private viewedExampleTourSteps: Set<string> = new Set();
|
|
4205
|
+
private exampleThreadProviders: Map<string, ThreadDebuggerProvider> =
|
|
4206
|
+
new Map();
|
|
4207
|
+
private exampleTourDismissed = false;
|
|
4208
|
+
private exampleTourActive = false;
|
|
4209
|
+
private exampleTourStep = 0;
|
|
4210
|
+
private exampleTourAutoShown = false;
|
|
4211
|
+
private threadsExampleOverviewVideoShouldLoad = false;
|
|
4212
|
+
private threadsExampleOverviewVideoReady = false;
|
|
4213
|
+
private threadsExampleOverviewVideoLoadTimer: number | null = null;
|
|
4214
|
+
private threadsExampleOverviewVideoIdleCallbackId: number | null = null;
|
|
4215
|
+
|
|
4216
|
+
get core(): CopilotKitCore | null {
|
|
4217
|
+
return this._core;
|
|
4218
|
+
}
|
|
4219
|
+
|
|
4220
|
+
set core(value: CopilotKitCore | null) {
|
|
4221
|
+
const oldValue = this._core;
|
|
4222
|
+
if (oldValue === value) {
|
|
4223
|
+
return;
|
|
4224
|
+
}
|
|
4225
|
+
|
|
4226
|
+
this.detachFromCore();
|
|
4227
|
+
|
|
4228
|
+
this._core = value ?? null;
|
|
4229
|
+
this.requestUpdate("core", oldValue);
|
|
4230
|
+
|
|
4231
|
+
if (this._core) {
|
|
4232
|
+
this.attachToCore(this._core);
|
|
4233
|
+
}
|
|
4234
|
+
}
|
|
4235
|
+
|
|
4236
|
+
private readonly contextState: Record<ContextKey, ContextState> = {
|
|
4237
|
+
button: {
|
|
4238
|
+
position: { x: EDGE_MARGIN, y: EDGE_MARGIN },
|
|
4239
|
+
size: { ...DEFAULT_BUTTON_SIZE },
|
|
4240
|
+
anchor: { horizontal: "right", vertical: "top" },
|
|
4241
|
+
anchorOffset: { x: EDGE_MARGIN, y: EDGE_MARGIN },
|
|
4242
|
+
},
|
|
4243
|
+
window: {
|
|
4244
|
+
position: { x: EDGE_MARGIN, y: EDGE_MARGIN },
|
|
4245
|
+
size: { ...DEFAULT_WINDOW_SIZE },
|
|
4246
|
+
anchor: { horizontal: "right", vertical: "top" },
|
|
4247
|
+
anchorOffset: { x: EDGE_MARGIN, y: EDGE_MARGIN },
|
|
4248
|
+
},
|
|
4249
|
+
};
|
|
4250
|
+
|
|
4251
|
+
private hasCustomPosition: Record<ContextKey, boolean> = {
|
|
3581
4252
|
button: false,
|
|
3582
4253
|
window: false,
|
|
3583
4254
|
};
|
|
@@ -3619,6 +4290,7 @@ export class WebInspectorElement extends LitElement {
|
|
|
3619
4290
|
label: "Threads",
|
|
3620
4291
|
icon: "MessageSquare" as LucideIconName,
|
|
3621
4292
|
},
|
|
4293
|
+
{ key: "memories", label: "Learning", icon: "Brain" as LucideIconName },
|
|
3622
4294
|
];
|
|
3623
4295
|
}
|
|
3624
4296
|
|
|
@@ -3634,6 +4306,31 @@ export class WebInspectorElement extends LitElement {
|
|
|
3634
4306
|
return this.getThreadServiceStatus() !== "unavailable";
|
|
3635
4307
|
}
|
|
3636
4308
|
|
|
4309
|
+
private getActiveThreadsState(): {
|
|
4310
|
+
displayThreads: ɵThread[];
|
|
4311
|
+
threadsErrorMessage: string | null;
|
|
4312
|
+
} {
|
|
4313
|
+
const displayThreads =
|
|
4314
|
+
this.selectedContext === "all-agents"
|
|
4315
|
+
? this._threads
|
|
4316
|
+
: (this._threadsByAgent.get(this.selectedContext) ?? []);
|
|
4317
|
+
|
|
4318
|
+
// Surface a thread-store load error inline. For "all-agents" we report
|
|
4319
|
+
// the first error encountered across all agents (good enough for a
|
|
4320
|
+
// debugging surface — the per-agent context filter narrows down the
|
|
4321
|
+
// culprit). For a specific agent we use that agent's error directly.
|
|
4322
|
+
let threadsErrorMessage: string | null = null;
|
|
4323
|
+
if (this.selectedContext === "all-agents") {
|
|
4324
|
+
const firstError = this._threadsErrorByAgent.values().next().value;
|
|
4325
|
+
threadsErrorMessage = firstError?.message ?? null;
|
|
4326
|
+
} else {
|
|
4327
|
+
threadsErrorMessage =
|
|
4328
|
+
this._threadsErrorByAgent.get(this.selectedContext)?.message ?? null;
|
|
4329
|
+
}
|
|
4330
|
+
|
|
4331
|
+
return { displayThreads, threadsErrorMessage };
|
|
4332
|
+
}
|
|
4333
|
+
|
|
3637
4334
|
private getThreadsTelemetryProps(
|
|
3638
4335
|
extra: Partial<InspectorThreadTelemetryProps> = {},
|
|
3639
4336
|
options: { includeUrlAttribution?: boolean } = {},
|
|
@@ -3660,12 +4357,45 @@ export class WebInspectorElement extends LitElement {
|
|
|
3660
4357
|
};
|
|
3661
4358
|
}
|
|
3662
4359
|
|
|
3663
|
-
private
|
|
3664
|
-
|
|
4360
|
+
private getMemoriesTelemetryProps(): InspectorMemoryTelemetryProps {
|
|
4361
|
+
const distinctId = !this.core?.telemetryDisabled
|
|
4362
|
+
? getTelemetryDistinctIdForUrl()
|
|
4363
|
+
: null;
|
|
4364
|
+
return {
|
|
4365
|
+
posthog_distinct_id: distinctId ?? undefined,
|
|
4366
|
+
memory_count: this._memories.length,
|
|
4367
|
+
available: this._memoriesAvailable,
|
|
4368
|
+
};
|
|
4369
|
+
}
|
|
4370
|
+
|
|
4371
|
+
private getIntelligenceSignupUrl(): string {
|
|
4372
|
+
return this.appendRefParam(INTELLIGENCE_SIGNUP_URL, "cpk-inspector");
|
|
4373
|
+
}
|
|
4374
|
+
|
|
4375
|
+
private getThreadsIntelligenceSignupUrl(): string {
|
|
4376
|
+
return this.appendRefParam(
|
|
4377
|
+
THREADS_INTELLIGENCE_SIGNIN_URL,
|
|
4378
|
+
"cpk-inspector",
|
|
4379
|
+
);
|
|
4380
|
+
}
|
|
4381
|
+
|
|
4382
|
+
private getTalkToEngineerUrl(): string {
|
|
4383
|
+
return this.appendRefParam(TALK_TO_ENGINEER_URL, "cpk-inspector-threads");
|
|
4384
|
+
}
|
|
4385
|
+
|
|
4386
|
+
private getThreadsTalkToEngineerUrl(): string {
|
|
4387
|
+
return this.appendRefParam(TALK_TO_ENGINEER_URL, "cpk-inspector-threads");
|
|
4388
|
+
}
|
|
4389
|
+
|
|
4390
|
+
private getThreadsDocsUrl(): string {
|
|
4391
|
+
return this.appendRefParam(THREADS_DOCS_URL, "cpk-inspector-threads");
|
|
3665
4392
|
}
|
|
3666
4393
|
|
|
3667
|
-
private
|
|
3668
|
-
return this.appendRefParam(
|
|
4394
|
+
private getSelfHostedIntelligenceUrl(): string {
|
|
4395
|
+
return this.appendRefParam(
|
|
4396
|
+
SELF_HOSTED_INTELLIGENCE_URL,
|
|
4397
|
+
"cpk-inspector-threads",
|
|
4398
|
+
);
|
|
3669
4399
|
}
|
|
3670
4400
|
|
|
3671
4401
|
private subscribeToThreadStore(agentId: string, store: ɵThreadStore): void {
|
|
@@ -3706,6 +4436,7 @@ export class WebInspectorElement extends LitElement {
|
|
|
3706
4436
|
if (this._threads.length === 0) return;
|
|
3707
4437
|
const stillValid =
|
|
3708
4438
|
this.selectedThreadId != null &&
|
|
4439
|
+
!this.isExampleThreadId(this.selectedThreadId) &&
|
|
3709
4440
|
this._threads.some((t) => t.id === this.selectedThreadId);
|
|
3710
4441
|
if (!stillValid) {
|
|
3711
4442
|
// Threads are sorted most-recently-updated first
|
|
@@ -3886,6 +4617,91 @@ export class WebInspectorElement extends LitElement {
|
|
|
3886
4617
|
if (core.context) {
|
|
3887
4618
|
this.contextStore = this.normalizeContextStore(core.context);
|
|
3888
4619
|
}
|
|
4620
|
+
|
|
4621
|
+
// NOTE: the memory store is intentionally NOT touched here. Calling
|
|
4622
|
+
// `core.getMemoryStore()` lazily creates + `.start()`s the store and opens
|
|
4623
|
+
// a realtime connection, so merely attaching the inspector would spin up a
|
|
4624
|
+
// memory store + realtime even in apps that never use memory. Instead, the
|
|
4625
|
+
// store is created + subscribed on first Memories-tab activation via
|
|
4626
|
+
// `ensureMemorySubscription` (user-initiated, acceptable). Attaching the
|
|
4627
|
+
// inspector creates nothing.
|
|
4628
|
+
//
|
|
4629
|
+
// Exception: if the Memories tab is ALREADY the active tab when core is
|
|
4630
|
+
// wired (e.g. core attaches after `firstUpdated` restored a persisted
|
|
4631
|
+
// `selectedMenu: "memories"`), subscribe now so the live store + realtime
|
|
4632
|
+
// status paint instead of the stuck defaults. This preserves INSP-1 (no
|
|
4633
|
+
// unconditional subscribe on attach) because it is gated on the active tab,
|
|
4634
|
+
// and is safe to call when already subscribed — `ensureMemorySubscription`
|
|
4635
|
+
// early-returns on `_memorySubscribed`.
|
|
4636
|
+
if (this.selectedMenu === "memories") {
|
|
4637
|
+
this.ensureMemorySubscription();
|
|
4638
|
+
}
|
|
4639
|
+
}
|
|
4640
|
+
|
|
4641
|
+
/**
|
|
4642
|
+
* Lazily subscribes to the singleton memory store the first time the user
|
|
4643
|
+
* activates the Memories tab. This is deferred out of `attachToCore` because
|
|
4644
|
+
* `core.getMemoryStore()` is what creates + starts the store and opens
|
|
4645
|
+
* realtime — doing it on attach would start memory for apps that never use
|
|
4646
|
+
* it. Idempotent: repeated tab activations are guarded by
|
|
4647
|
+
* `_memorySubscribed`. On an older @copilotkit/core without `getMemoryStore`,
|
|
4648
|
+
* records the unsupported state so the teaser can guide an SDK upgrade.
|
|
4649
|
+
*/
|
|
4650
|
+
private ensureMemorySubscription(): void {
|
|
4651
|
+
if (this._memorySubscribed) {
|
|
4652
|
+
return;
|
|
4653
|
+
}
|
|
4654
|
+
const core = this._core;
|
|
4655
|
+
if (!core) {
|
|
4656
|
+
return;
|
|
4657
|
+
}
|
|
4658
|
+
|
|
4659
|
+
// Guard like getThreadStores: older @copilotkit/core has no getMemoryStore.
|
|
4660
|
+
// When absent, flag the unsupported state so the teaser shows upgrade copy
|
|
4661
|
+
// instead of throwing a TypeError that would break the entire inspector.
|
|
4662
|
+
if (typeof core.getMemoryStore !== "function") {
|
|
4663
|
+
this._memoryStoreUnsupported = true;
|
|
4664
|
+
this._memoriesAvailable = false;
|
|
4665
|
+
this.requestUpdate();
|
|
4666
|
+
return;
|
|
4667
|
+
}
|
|
4668
|
+
|
|
4669
|
+
this._memorySubscribed = true;
|
|
4670
|
+
this._memoryStoreUnsupported = false;
|
|
4671
|
+
|
|
4672
|
+
// First touch of getMemoryStore() — creates + starts the store, opens realtime.
|
|
4673
|
+
const memoryStore = core.getMemoryStore();
|
|
4674
|
+
const ms = memoryStore.getState();
|
|
4675
|
+
this._memories = ɵselectMemories(ms);
|
|
4676
|
+
this._memoriesLoading = ɵselectMemoriesIsLoading(ms);
|
|
4677
|
+
this._memoriesError = ɵselectMemoriesError(ms);
|
|
4678
|
+
this._memoriesAvailable = ɵselectMemoriesAvailable(ms);
|
|
4679
|
+
this._memoriesRealtimeStatus = ɵselectMemoriesRealtimeStatus(ms);
|
|
4680
|
+
const memSubs = [
|
|
4681
|
+
memoryStore.select(ɵselectMemories).subscribe((v) => {
|
|
4682
|
+
this._memories = v;
|
|
4683
|
+
this.requestUpdate();
|
|
4684
|
+
}),
|
|
4685
|
+
memoryStore.select(ɵselectMemoriesIsLoading).subscribe((v) => {
|
|
4686
|
+
this._memoriesLoading = v;
|
|
4687
|
+
this.requestUpdate();
|
|
4688
|
+
}),
|
|
4689
|
+
memoryStore.select(ɵselectMemoriesError).subscribe((v) => {
|
|
4690
|
+
this._memoriesError = v;
|
|
4691
|
+
this.requestUpdate();
|
|
4692
|
+
}),
|
|
4693
|
+
memoryStore.select(ɵselectMemoriesAvailable).subscribe((v) => {
|
|
4694
|
+
this._memoriesAvailable = v;
|
|
4695
|
+
this.requestUpdate();
|
|
4696
|
+
}),
|
|
4697
|
+
// Group E — realtime connection health.
|
|
4698
|
+
memoryStore.select(ɵselectMemoriesRealtimeStatus).subscribe((v) => {
|
|
4699
|
+
this._memoriesRealtimeStatus = v;
|
|
4700
|
+
this.requestUpdate();
|
|
4701
|
+
}),
|
|
4702
|
+
];
|
|
4703
|
+
this._memoryUnsub = () => memSubs.forEach((s) => s.unsubscribe());
|
|
4704
|
+
this.requestUpdate();
|
|
3889
4705
|
}
|
|
3890
4706
|
|
|
3891
4707
|
private detachFromCore(): void {
|
|
@@ -3893,6 +4709,17 @@ export class WebInspectorElement extends LitElement {
|
|
|
3893
4709
|
this.coreUnsubscribe();
|
|
3894
4710
|
this.coreUnsubscribe = null;
|
|
3895
4711
|
}
|
|
4712
|
+
this._memoryUnsub?.();
|
|
4713
|
+
this._memoryUnsub = null;
|
|
4714
|
+
this._memories = [];
|
|
4715
|
+
this._memoriesLoading = false;
|
|
4716
|
+
this._memoriesError = null;
|
|
4717
|
+
this._memoriesAvailable = true;
|
|
4718
|
+
this._memoriesRealtimeStatus = "connecting";
|
|
4719
|
+
// Reset the lazy-subscription guards so a later attach + Memories-tab
|
|
4720
|
+
// activation re-subscribes (and re-evaluates SDK support) cleanly.
|
|
4721
|
+
this._memorySubscribed = false;
|
|
4722
|
+
this._memoryStoreUnsupported = false;
|
|
3896
4723
|
this.coreSubscriber = null;
|
|
3897
4724
|
this.runtimeStatus = null;
|
|
3898
4725
|
this.lastCoreError = null;
|
|
@@ -5199,6 +6026,35 @@ ${argsString}</pre
|
|
|
5199
6026
|
.cpk-tab-active {
|
|
5200
6027
|
cursor: pointer;
|
|
5201
6028
|
}
|
|
6029
|
+
.cpk-threads-overview-video-frame {
|
|
6030
|
+
position: relative;
|
|
6031
|
+
display: block;
|
|
6032
|
+
width: 100%;
|
|
6033
|
+
max-width: 440px;
|
|
6034
|
+
aspect-ratio: 16 / 9;
|
|
6035
|
+
margin: 0 0 14px;
|
|
6036
|
+
overflow: hidden;
|
|
6037
|
+
border: 1px solid #dbdbe5;
|
|
6038
|
+
border-radius: 8px;
|
|
6039
|
+
background:
|
|
6040
|
+
linear-gradient(
|
|
6041
|
+
135deg,
|
|
6042
|
+
rgba(190, 194, 255, 0.18),
|
|
6043
|
+
rgba(133, 236, 206, 0.12)
|
|
6044
|
+
),
|
|
6045
|
+
#ffffff;
|
|
6046
|
+
box-shadow: 0 8px 20px rgba(1, 5, 7, 0.08);
|
|
6047
|
+
}
|
|
6048
|
+
.cpk-threads-overview-video {
|
|
6049
|
+
width: 100%;
|
|
6050
|
+
height: 100%;
|
|
6051
|
+
object-fit: cover;
|
|
6052
|
+
opacity: 0;
|
|
6053
|
+
transition: opacity 220ms ease;
|
|
6054
|
+
}
|
|
6055
|
+
.cpk-threads-overview-video[data-ready="true"] {
|
|
6056
|
+
opacity: 1;
|
|
6057
|
+
}
|
|
5202
6058
|
|
|
5203
6059
|
/* ── Header control buttons (dock, close) — first row only ───── */
|
|
5204
6060
|
.drag-handle > div:first-child button {
|
|
@@ -5381,6 +6237,7 @@ ${argsString}</pre
|
|
|
5381
6237
|
|
|
5382
6238
|
// Load state early (before first render) so menu selection is correct
|
|
5383
6239
|
this.hydrateStateFromStorageEarly();
|
|
6240
|
+
this.exampleTourDismissed = this.readThreadsExampleTourDismissed();
|
|
5384
6241
|
this.tryAutoAttachCore();
|
|
5385
6242
|
this.ensureAnnouncementLoading();
|
|
5386
6243
|
}
|
|
@@ -5415,6 +6272,17 @@ ${argsString}</pre
|
|
|
5415
6272
|
clearTimeout(this.transitionTimeoutId);
|
|
5416
6273
|
this.transitionTimeoutId = null;
|
|
5417
6274
|
}
|
|
6275
|
+
if (this.threadsExampleOverviewVideoLoadTimer !== null) {
|
|
6276
|
+
window.clearTimeout(this.threadsExampleOverviewVideoLoadTimer);
|
|
6277
|
+
this.threadsExampleOverviewVideoLoadTimer = null;
|
|
6278
|
+
}
|
|
6279
|
+
if (
|
|
6280
|
+
this.threadsExampleOverviewVideoIdleCallbackId !== null &&
|
|
6281
|
+
typeof window.cancelIdleCallback === "function"
|
|
6282
|
+
) {
|
|
6283
|
+
window.cancelIdleCallback(this.threadsExampleOverviewVideoIdleCallbackId);
|
|
6284
|
+
this.threadsExampleOverviewVideoIdleCallbackId = null;
|
|
6285
|
+
}
|
|
5418
6286
|
this.removeDockStyles(true); // Clean up any docking styles, skip transition
|
|
5419
6287
|
this.detachFromCore();
|
|
5420
6288
|
}
|
|
@@ -5439,6 +6307,18 @@ ${argsString}</pre
|
|
|
5439
6307
|
|
|
5440
6308
|
this.hydrateStateFromStorage();
|
|
5441
6309
|
|
|
6310
|
+
// `hydrateStateFromStorage` may have restored `selectedMenu: "memories"`.
|
|
6311
|
+
// The memory subscription is normally created on a Memories-tab CLICK via
|
|
6312
|
+
// `handleMenuSelect`, which never fires when the tab boots already active —
|
|
6313
|
+
// leaving the realtime indicator stuck on the default "connecting" and the
|
|
6314
|
+
// list empty until the user toggles tabs. Subscribe now when the Memories
|
|
6315
|
+
// tab is the active tab. Gated on the active tab to preserve INSP-1 (no
|
|
6316
|
+
// unconditional subscribe), and safe if core is not yet attached or already
|
|
6317
|
+
// subscribed — `ensureMemorySubscription` early-returns in both cases.
|
|
6318
|
+
if (this.selectedMenu === "memories") {
|
|
6319
|
+
this.ensureMemorySubscription();
|
|
6320
|
+
}
|
|
6321
|
+
|
|
5442
6322
|
// Apply docking styles if open and docked (skip transition on initial load)
|
|
5443
6323
|
if (this.isOpen && this.dockMode !== "floating") {
|
|
5444
6324
|
this.applyDockStyles(true);
|
|
@@ -5672,6 +6552,21 @@ ${argsString}</pre
|
|
|
5672
6552
|
</button>
|
|
5673
6553
|
`;
|
|
5674
6554
|
})}
|
|
6555
|
+
${
|
|
6556
|
+
this.selectedMenu === "threads"
|
|
6557
|
+
? html`
|
|
6558
|
+
<a
|
|
6559
|
+
href=${this.getThreadsTalkToEngineerUrl()}
|
|
6560
|
+
target="_blank"
|
|
6561
|
+
rel="noopener"
|
|
6562
|
+
style="margin-left:auto;display:inline-flex;align-items:center;gap:6px;border-radius:6px;border:1px solid #dbdbe5;background:#ffffff;padding:7px 10px;font-size:12px;font-weight:600;color:#57575b;text-decoration:none;"
|
|
6563
|
+
@click=${this.handleTalkToEngineerClick}
|
|
6564
|
+
>
|
|
6565
|
+
Talk to an Engineer
|
|
6566
|
+
</a>
|
|
6567
|
+
`
|
|
6568
|
+
: nothing
|
|
6569
|
+
}
|
|
5675
6570
|
</div>
|
|
5676
6571
|
</div>
|
|
5677
6572
|
<div class="flex flex-1 flex-col overflow-hidden">
|
|
@@ -6896,6 +7791,10 @@ ${argsString}</pre
|
|
|
6896
7791
|
return this.renderThreadsView();
|
|
6897
7792
|
}
|
|
6898
7793
|
|
|
7794
|
+
if (this.selectedMenu === "memories") {
|
|
7795
|
+
return this.renderMemoriesView();
|
|
7796
|
+
}
|
|
7797
|
+
|
|
6899
7798
|
if (this.selectedMenu === "settings") {
|
|
6900
7799
|
return this.renderSettingsPanel();
|
|
6901
7800
|
}
|
|
@@ -7025,25 +7924,509 @@ ${argsString}</pre
|
|
|
7025
7924
|
if (target.hasPointerCapture(this.threadDividerPointerId)) {
|
|
7026
7925
|
target.releasePointerCapture(this.threadDividerPointerId);
|
|
7027
7926
|
}
|
|
7028
|
-
this.threadDividerResizing = false;
|
|
7029
|
-
};
|
|
7927
|
+
this.threadDividerResizing = false;
|
|
7928
|
+
};
|
|
7929
|
+
|
|
7930
|
+
private trackThreadsViewStateOnce(
|
|
7931
|
+
state: "locked" | "empty_enabled" | "enabled",
|
|
7932
|
+
threadCount: number,
|
|
7933
|
+
): void {
|
|
7934
|
+
if (this.core?.telemetryDisabled) return;
|
|
7935
|
+
const key = `${state}:${this.getThreadServiceStatus()}`;
|
|
7936
|
+
if (this.viewedThreadsTelemetryStates.has(key)) return;
|
|
7937
|
+
this.viewedThreadsTelemetryStates.add(key);
|
|
7938
|
+
const props = this.getThreadsTelemetryProps({ thread_count: threadCount });
|
|
7939
|
+
if (state === "locked") {
|
|
7940
|
+
trackThreadsLockedViewed(props);
|
|
7941
|
+
} else if (state === "empty_enabled") {
|
|
7942
|
+
trackThreadsEmptyEnabledViewed(props);
|
|
7943
|
+
} else {
|
|
7944
|
+
trackThreadsEnabledViewed(props);
|
|
7945
|
+
}
|
|
7946
|
+
}
|
|
7947
|
+
|
|
7948
|
+
private shouldRenderExampleThreads(
|
|
7949
|
+
displayThreads: ɵThread[],
|
|
7950
|
+
threadsErrorMessage: string | null,
|
|
7951
|
+
): boolean {
|
|
7952
|
+
return (
|
|
7953
|
+
this.areThreadEndpointsAvailable() &&
|
|
7954
|
+
!threadsErrorMessage &&
|
|
7955
|
+
displayThreads.length === 0
|
|
7956
|
+
);
|
|
7957
|
+
}
|
|
7958
|
+
|
|
7959
|
+
private getVisibleThreads(
|
|
7960
|
+
displayThreads: ɵThread[],
|
|
7961
|
+
threadsErrorMessage: string | null,
|
|
7962
|
+
): ɵThread[] {
|
|
7963
|
+
return this.shouldRenderExampleThreads(displayThreads, threadsErrorMessage)
|
|
7964
|
+
? THREADS_EXAMPLE_THREADS
|
|
7965
|
+
: displayThreads;
|
|
7966
|
+
}
|
|
7967
|
+
|
|
7968
|
+
private isExampleThreadId(threadId: string | null | undefined): boolean {
|
|
7969
|
+
return THREADS_EXAMPLE_THREADS.some((thread) => thread.id === threadId);
|
|
7970
|
+
}
|
|
7971
|
+
|
|
7972
|
+
private getExampleThreadProvider(threadId: string): ThreadDebuggerProvider {
|
|
7973
|
+
const cached = this.exampleThreadProviders.get(threadId);
|
|
7974
|
+
if (cached) return cached;
|
|
7975
|
+
const thread = THREADS_EXAMPLE_THREADS.find((item) => item.id === threadId);
|
|
7976
|
+
const details = THREADS_EXAMPLE_DETAILS[threadId];
|
|
7977
|
+
const provider: ThreadDebuggerProvider = {
|
|
7978
|
+
getThreadMetadata: async () =>
|
|
7979
|
+
thread
|
|
7980
|
+
? {
|
|
7981
|
+
id: thread.id,
|
|
7982
|
+
name: thread.name,
|
|
7983
|
+
agentId: thread.agentId,
|
|
7984
|
+
endUserId: "example-user",
|
|
7985
|
+
status: "completed",
|
|
7986
|
+
updatedAt: thread.updatedAt,
|
|
7987
|
+
}
|
|
7988
|
+
: null,
|
|
7989
|
+
getMessages: async () => details?.messages ?? [],
|
|
7990
|
+
getEvents: async () => details?.events ?? [],
|
|
7991
|
+
getState: async () => details?.state ?? null,
|
|
7992
|
+
};
|
|
7993
|
+
this.exampleThreadProviders.set(threadId, provider);
|
|
7994
|
+
return provider;
|
|
7995
|
+
}
|
|
7996
|
+
|
|
7997
|
+
private trackThreadsExampleViewedOnce(): void {
|
|
7998
|
+
if (this.core?.telemetryDisabled) return;
|
|
7999
|
+
for (const thread of THREADS_EXAMPLE_THREADS) {
|
|
8000
|
+
if (this.viewedExampleThreadIds.has(thread.id)) continue;
|
|
8001
|
+
this.viewedExampleThreadIds.add(thread.id);
|
|
8002
|
+
trackThreadsExampleViewed(
|
|
8003
|
+
this.getThreadsTelemetryProps({
|
|
8004
|
+
example_thread_id: thread.id,
|
|
8005
|
+
thread_count: 0,
|
|
8006
|
+
}),
|
|
8007
|
+
);
|
|
8008
|
+
}
|
|
8009
|
+
}
|
|
8010
|
+
|
|
8011
|
+
private trackThreadsExampleSelectedOnce(threadId: string): void {
|
|
8012
|
+
if (this.core?.telemetryDisabled) return;
|
|
8013
|
+
if (this.selectedExampleThreadIds.has(threadId)) return;
|
|
8014
|
+
this.selectedExampleThreadIds.add(threadId);
|
|
8015
|
+
trackThreadsExampleSelected(
|
|
8016
|
+
this.getThreadsTelemetryProps({
|
|
8017
|
+
example_thread_id: threadId,
|
|
8018
|
+
thread_count: 0,
|
|
8019
|
+
}),
|
|
8020
|
+
);
|
|
8021
|
+
}
|
|
8022
|
+
|
|
8023
|
+
private getCurrentExampleTourProps(): InspectorThreadTelemetryProps {
|
|
8024
|
+
const step =
|
|
8025
|
+
THREADS_EXAMPLE_TOUR_STEPS[this.exampleTourStep] ??
|
|
8026
|
+
THREADS_EXAMPLE_TOUR_STEPS[0]!;
|
|
8027
|
+
return this.getThreadsTelemetryProps({
|
|
8028
|
+
example_thread_id: this.selectedThreadId ?? undefined,
|
|
8029
|
+
thread_count: 0,
|
|
8030
|
+
tour_step: this.exampleTourStep + 1,
|
|
8031
|
+
tour_tab: step?.tab,
|
|
8032
|
+
});
|
|
8033
|
+
}
|
|
8034
|
+
|
|
8035
|
+
private trackThreadsExampleTourStepViewedOnce(): void {
|
|
8036
|
+
if (this.core?.telemetryDisabled || !this.selectedThreadId) return;
|
|
8037
|
+
const step =
|
|
8038
|
+
THREADS_EXAMPLE_TOUR_STEPS[this.exampleTourStep] ??
|
|
8039
|
+
THREADS_EXAMPLE_TOUR_STEPS[0]!;
|
|
8040
|
+
if (!step) return;
|
|
8041
|
+
const key = `${this.selectedThreadId}:${this.exampleTourStep}`;
|
|
8042
|
+
if (this.viewedExampleTourSteps.has(key)) return;
|
|
8043
|
+
this.viewedExampleTourSteps.add(key);
|
|
8044
|
+
trackThreadsExampleTourStepViewed(this.getCurrentExampleTourProps());
|
|
8045
|
+
}
|
|
8046
|
+
|
|
8047
|
+
private syncExampleTourTab(): void {
|
|
8048
|
+
const step =
|
|
8049
|
+
THREADS_EXAMPLE_TOUR_STEPS[this.exampleTourStep] ??
|
|
8050
|
+
THREADS_EXAMPLE_TOUR_STEPS[0]!;
|
|
8051
|
+
if (!step) return;
|
|
8052
|
+
void this.updateComplete.then(() => {
|
|
8053
|
+
const details = this.shadowRoot?.querySelector("cpk-thread-details") as
|
|
8054
|
+
| (ɵCpkThreadDetails & { selectTab?: (id: ThreadDetailsTab) => void })
|
|
8055
|
+
| null;
|
|
8056
|
+
details?.selectTab?.(step.tab);
|
|
8057
|
+
});
|
|
8058
|
+
}
|
|
8059
|
+
|
|
8060
|
+
private startExampleTour(autoStarted: boolean): void {
|
|
8061
|
+
if (!this.selectedThreadId) return;
|
|
8062
|
+
this.exampleTourActive = true;
|
|
8063
|
+
this.exampleTourStep = 0;
|
|
8064
|
+
if (autoStarted) {
|
|
8065
|
+
this.exampleTourAutoShown = true;
|
|
8066
|
+
if (!this.core?.telemetryDisabled) {
|
|
8067
|
+
trackThreadsExampleTourStarted(this.getCurrentExampleTourProps());
|
|
8068
|
+
}
|
|
8069
|
+
} else if (!this.core?.telemetryDisabled) {
|
|
8070
|
+
trackThreadsExampleTourReopened(this.getCurrentExampleTourProps());
|
|
8071
|
+
}
|
|
8072
|
+
this.trackThreadsExampleTourStepViewedOnce();
|
|
8073
|
+
this.syncExampleTourTab();
|
|
8074
|
+
this.requestUpdate();
|
|
8075
|
+
}
|
|
8076
|
+
|
|
8077
|
+
private setExampleTourStep(nextStep: number): void {
|
|
8078
|
+
this.exampleTourStep = Math.max(
|
|
8079
|
+
0,
|
|
8080
|
+
Math.min(THREADS_EXAMPLE_TOUR_STEPS.length - 1, nextStep),
|
|
8081
|
+
);
|
|
8082
|
+
this.trackThreadsExampleTourStepViewedOnce();
|
|
8083
|
+
this.syncExampleTourTab();
|
|
8084
|
+
this.requestUpdate();
|
|
8085
|
+
}
|
|
8086
|
+
|
|
8087
|
+
private dismissExampleTour(method: "skip" | "done"): void {
|
|
8088
|
+
if (!this.selectedThreadId) return;
|
|
8089
|
+
const props = {
|
|
8090
|
+
...this.getCurrentExampleTourProps(),
|
|
8091
|
+
dismiss_method: method,
|
|
8092
|
+
};
|
|
8093
|
+
this.exampleTourActive = false;
|
|
8094
|
+
this.exampleTourDismissed = true;
|
|
8095
|
+
this.writeThreadsExampleTourDismissed();
|
|
8096
|
+
if (!this.core?.telemetryDisabled) {
|
|
8097
|
+
if (method === "done") {
|
|
8098
|
+
trackThreadsExampleTourCompleted(props);
|
|
8099
|
+
} else {
|
|
8100
|
+
trackThreadsExampleTourDismissed(props);
|
|
8101
|
+
}
|
|
8102
|
+
}
|
|
8103
|
+
this.requestUpdate();
|
|
8104
|
+
}
|
|
8105
|
+
|
|
8106
|
+
private handleThreadsThreadSelected(
|
|
8107
|
+
threadId: string,
|
|
8108
|
+
showingExamples: boolean,
|
|
8109
|
+
): void {
|
|
8110
|
+
if (showingExamples && this.selectedThreadId === threadId) {
|
|
8111
|
+
this.selectedThreadId = null;
|
|
8112
|
+
this.exampleTourActive = false;
|
|
8113
|
+
this.requestUpdate();
|
|
8114
|
+
return;
|
|
8115
|
+
}
|
|
8116
|
+
|
|
8117
|
+
this.selectedThreadId = threadId;
|
|
8118
|
+
if (showingExamples && this.isExampleThreadId(threadId)) {
|
|
8119
|
+
this.trackThreadsExampleSelectedOnce(threadId);
|
|
8120
|
+
if (!this.exampleTourDismissed && !this.exampleTourAutoShown) {
|
|
8121
|
+
this.startExampleTour(true);
|
|
8122
|
+
} else {
|
|
8123
|
+
this.exampleTourActive = false;
|
|
8124
|
+
}
|
|
8125
|
+
} else {
|
|
8126
|
+
this.exampleTourActive = false;
|
|
8127
|
+
}
|
|
8128
|
+
this.requestUpdate();
|
|
8129
|
+
}
|
|
8130
|
+
|
|
8131
|
+
private readThreadsExampleTourDismissed(): boolean {
|
|
8132
|
+
if (typeof window === "undefined") return false;
|
|
8133
|
+
try {
|
|
8134
|
+
const raw = window.localStorage.getItem(THREADS_EXAMPLE_TOUR_STORAGE_KEY);
|
|
8135
|
+
if (!raw) return false;
|
|
8136
|
+
const value = JSON.parse(raw) as { dismissed?: unknown };
|
|
8137
|
+
return value.dismissed === true;
|
|
8138
|
+
} catch {
|
|
8139
|
+
return false;
|
|
8140
|
+
}
|
|
8141
|
+
}
|
|
8142
|
+
|
|
8143
|
+
private writeThreadsExampleTourDismissed(): void {
|
|
8144
|
+
if (typeof window === "undefined") return;
|
|
8145
|
+
try {
|
|
8146
|
+
window.localStorage.setItem(
|
|
8147
|
+
THREADS_EXAMPLE_TOUR_STORAGE_KEY,
|
|
8148
|
+
JSON.stringify({ dismissed: true }),
|
|
8149
|
+
);
|
|
8150
|
+
} catch {
|
|
8151
|
+
// Persistence is best-effort; the inspector should keep working without it.
|
|
8152
|
+
}
|
|
8153
|
+
}
|
|
8154
|
+
|
|
8155
|
+
private scheduleThreadsExampleOverviewVideoLoad(): void {
|
|
8156
|
+
if (
|
|
8157
|
+
this.threadsExampleOverviewVideoShouldLoad ||
|
|
8158
|
+
this.threadsExampleOverviewVideoLoadTimer !== null ||
|
|
8159
|
+
this.threadsExampleOverviewVideoIdleCallbackId !== null ||
|
|
8160
|
+
typeof window === "undefined"
|
|
8161
|
+
) {
|
|
8162
|
+
return;
|
|
8163
|
+
}
|
|
8164
|
+
|
|
8165
|
+
if (window.matchMedia?.("(prefers-reduced-motion: reduce)").matches) {
|
|
8166
|
+
return;
|
|
8167
|
+
}
|
|
8168
|
+
|
|
8169
|
+
const loadVideo = () => {
|
|
8170
|
+
this.threadsExampleOverviewVideoLoadTimer = null;
|
|
8171
|
+
this.threadsExampleOverviewVideoIdleCallbackId = null;
|
|
8172
|
+
this.threadsExampleOverviewVideoShouldLoad = true;
|
|
8173
|
+
this.requestUpdate();
|
|
8174
|
+
};
|
|
8175
|
+
|
|
8176
|
+
if (typeof window.requestIdleCallback === "function") {
|
|
8177
|
+
this.threadsExampleOverviewVideoIdleCallbackId =
|
|
8178
|
+
window.requestIdleCallback(loadVideo, { timeout: 1200 });
|
|
8179
|
+
return;
|
|
8180
|
+
}
|
|
8181
|
+
|
|
8182
|
+
this.threadsExampleOverviewVideoLoadTimer = window.setTimeout(
|
|
8183
|
+
loadVideo,
|
|
8184
|
+
450,
|
|
8185
|
+
);
|
|
8186
|
+
}
|
|
8187
|
+
|
|
8188
|
+
private handleThreadsExampleOverviewVideoLoaded = (): void => {
|
|
8189
|
+
this.threadsExampleOverviewVideoReady = true;
|
|
8190
|
+
this.requestUpdate();
|
|
8191
|
+
};
|
|
8192
|
+
|
|
8193
|
+
private renderThreadsExampleOverviewVideo() {
|
|
8194
|
+
this.scheduleThreadsExampleOverviewVideoLoad();
|
|
8195
|
+
|
|
8196
|
+
return html`
|
|
8197
|
+
<div class="cpk-threads-overview-video-frame" aria-hidden="true">
|
|
8198
|
+
${
|
|
8199
|
+
this.threadsExampleOverviewVideoShouldLoad
|
|
8200
|
+
? html`
|
|
8201
|
+
<video
|
|
8202
|
+
class="cpk-threads-overview-video"
|
|
8203
|
+
data-ready=${this.threadsExampleOverviewVideoReady}
|
|
8204
|
+
src=${THREADS_EXAMPLE_OVERVIEW_VIDEO_URL}
|
|
8205
|
+
autoplay
|
|
8206
|
+
loop
|
|
8207
|
+
muted
|
|
8208
|
+
playsinline
|
|
8209
|
+
preload="metadata"
|
|
8210
|
+
@loadeddata=${this.handleThreadsExampleOverviewVideoLoaded}
|
|
8211
|
+
></video>
|
|
8212
|
+
`
|
|
8213
|
+
: nothing
|
|
8214
|
+
}
|
|
8215
|
+
</div>
|
|
8216
|
+
`;
|
|
8217
|
+
}
|
|
8218
|
+
|
|
8219
|
+
private renderThreadsExampleOverview() {
|
|
8220
|
+
return html`
|
|
8221
|
+
<div
|
|
8222
|
+
style="
|
|
8223
|
+
flex: 1;
|
|
8224
|
+
min-width: 0;
|
|
8225
|
+
display: flex;
|
|
8226
|
+
align-items: center;
|
|
8227
|
+
justify-content: center;
|
|
8228
|
+
padding: 32px;
|
|
8229
|
+
background: #f7f7f9;
|
|
8230
|
+
"
|
|
8231
|
+
>
|
|
8232
|
+
<div style="max-width: 440px; color: #57575b;">
|
|
8233
|
+
<h2
|
|
8234
|
+
style="
|
|
8235
|
+
margin: 0 0 10px;
|
|
8236
|
+
font-size: 20px;
|
|
8237
|
+
line-height: 1.25;
|
|
8238
|
+
font-weight: 600;
|
|
8239
|
+
color: #010507;
|
|
8240
|
+
"
|
|
8241
|
+
>
|
|
8242
|
+
Threads are persistent, inspectable conversations
|
|
8243
|
+
</h2>
|
|
8244
|
+
${this.renderThreadsExampleOverviewVideo()}
|
|
8245
|
+
<p
|
|
8246
|
+
style="
|
|
8247
|
+
margin: 0 0 16px;
|
|
8248
|
+
font-size: 13px;
|
|
8249
|
+
line-height: 1.55;
|
|
8250
|
+
color: #57575b;
|
|
8251
|
+
"
|
|
8252
|
+
>
|
|
8253
|
+
Take a tour with the example threads in the sidebar. Then, start
|
|
8254
|
+
chatting in your app to create the first real thread.
|
|
8255
|
+
</p>
|
|
8256
|
+
<div style="display:flex;flex-wrap:wrap;gap:8px;">
|
|
8257
|
+
<a
|
|
8258
|
+
href=${this.getThreadsDocsUrl()}
|
|
8259
|
+
target="_blank"
|
|
8260
|
+
rel="noopener"
|
|
8261
|
+
style="
|
|
8262
|
+
display: inline-flex;
|
|
8263
|
+
align-items: center;
|
|
8264
|
+
justify-content: center;
|
|
8265
|
+
gap: 6px;
|
|
8266
|
+
min-height: 34px;
|
|
8267
|
+
border-radius: 6px;
|
|
8268
|
+
background: #010507;
|
|
8269
|
+
padding: 8px 12px;
|
|
8270
|
+
font-size: 12px;
|
|
8271
|
+
font-weight: 600;
|
|
8272
|
+
color: #ffffff;
|
|
8273
|
+
text-decoration: none;
|
|
8274
|
+
"
|
|
8275
|
+
>
|
|
8276
|
+
Learn how Threads work
|
|
8277
|
+
</a>
|
|
8278
|
+
<a
|
|
8279
|
+
href=${this.getSelfHostedIntelligenceUrl()}
|
|
8280
|
+
target="_blank"
|
|
8281
|
+
rel="noopener"
|
|
8282
|
+
style="
|
|
8283
|
+
display: inline-flex;
|
|
8284
|
+
align-items: center;
|
|
8285
|
+
justify-content: center;
|
|
8286
|
+
gap: 6px;
|
|
8287
|
+
min-height: 34px;
|
|
8288
|
+
border-radius: 6px;
|
|
8289
|
+
border: 1px solid #dbdbe5;
|
|
8290
|
+
background: #ffffff;
|
|
8291
|
+
padding: 8px 12px;
|
|
8292
|
+
font-size: 12px;
|
|
8293
|
+
font-weight: 600;
|
|
8294
|
+
color: #010507;
|
|
8295
|
+
text-decoration: none;
|
|
8296
|
+
"
|
|
8297
|
+
>
|
|
8298
|
+
Explore self-hosted Intelligence
|
|
8299
|
+
</a>
|
|
8300
|
+
</div>
|
|
8301
|
+
</div>
|
|
8302
|
+
</div>
|
|
8303
|
+
`;
|
|
8304
|
+
}
|
|
8305
|
+
|
|
8306
|
+
private renderThreadsExampleTour() {
|
|
8307
|
+
if (
|
|
8308
|
+
!this.selectedThreadId ||
|
|
8309
|
+
!this.isExampleThreadId(this.selectedThreadId)
|
|
8310
|
+
) {
|
|
8311
|
+
return nothing;
|
|
8312
|
+
}
|
|
7030
8313
|
|
|
7031
|
-
|
|
7032
|
-
|
|
7033
|
-
|
|
7034
|
-
|
|
7035
|
-
|
|
7036
|
-
|
|
7037
|
-
|
|
7038
|
-
|
|
7039
|
-
|
|
7040
|
-
|
|
7041
|
-
|
|
7042
|
-
|
|
7043
|
-
|
|
7044
|
-
|
|
7045
|
-
|
|
8314
|
+
if (!this.exampleTourActive) {
|
|
8315
|
+
return html`
|
|
8316
|
+
<button
|
|
8317
|
+
type="button"
|
|
8318
|
+
style="
|
|
8319
|
+
position: absolute;
|
|
8320
|
+
right: 16px;
|
|
8321
|
+
bottom: 16px;
|
|
8322
|
+
z-index: 2;
|
|
8323
|
+
border: 1px solid #dbdbe5;
|
|
8324
|
+
border-radius: 6px;
|
|
8325
|
+
background: #ffffff;
|
|
8326
|
+
padding: 7px 10px;
|
|
8327
|
+
color: #57575b;
|
|
8328
|
+
font-size: 12px;
|
|
8329
|
+
font-weight: 600;
|
|
8330
|
+
cursor: pointer;
|
|
8331
|
+
box-shadow: 0 8px 18px rgba(1, 5, 7, 0.08);
|
|
8332
|
+
"
|
|
8333
|
+
@click=${() => this.startExampleTour(false)}
|
|
8334
|
+
>
|
|
8335
|
+
Show tour
|
|
8336
|
+
</button>
|
|
8337
|
+
`;
|
|
7046
8338
|
}
|
|
8339
|
+
|
|
8340
|
+
const step =
|
|
8341
|
+
THREADS_EXAMPLE_TOUR_STEPS[this.exampleTourStep] ??
|
|
8342
|
+
THREADS_EXAMPLE_TOUR_STEPS[0]!;
|
|
8343
|
+
const isFirst = this.exampleTourStep === 0;
|
|
8344
|
+
const isLast =
|
|
8345
|
+
this.exampleTourStep === THREADS_EXAMPLE_TOUR_STEPS.length - 1;
|
|
8346
|
+
|
|
8347
|
+
return html`
|
|
8348
|
+
<div
|
|
8349
|
+
role="dialog"
|
|
8350
|
+
aria-label="Example thread tour"
|
|
8351
|
+
style="
|
|
8352
|
+
position: absolute;
|
|
8353
|
+
right: 16px;
|
|
8354
|
+
bottom: 16px;
|
|
8355
|
+
z-index: 3;
|
|
8356
|
+
width: min(340px, calc(100% - 32px));
|
|
8357
|
+
border: 1px solid #dbdbe5;
|
|
8358
|
+
border-radius: 8px;
|
|
8359
|
+
background: #ffffff;
|
|
8360
|
+
padding: 14px;
|
|
8361
|
+
box-shadow: 0 16px 36px rgba(1, 5, 7, 0.14);
|
|
8362
|
+
color: #57575b;
|
|
8363
|
+
"
|
|
8364
|
+
>
|
|
8365
|
+
<div
|
|
8366
|
+
style="
|
|
8367
|
+
margin-bottom: 8px;
|
|
8368
|
+
font-family: 'Spline Sans Mono', monospace;
|
|
8369
|
+
font-size: 10px;
|
|
8370
|
+
font-weight: 600;
|
|
8371
|
+
color: #189370;
|
|
8372
|
+
text-transform: uppercase;
|
|
8373
|
+
"
|
|
8374
|
+
>
|
|
8375
|
+
${this.exampleTourStep + 1}/${THREADS_EXAMPLE_TOUR_STEPS.length}
|
|
8376
|
+
${step.label}
|
|
8377
|
+
</div>
|
|
8378
|
+
<div
|
|
8379
|
+
style="
|
|
8380
|
+
margin-bottom: 6px;
|
|
8381
|
+
font-size: 14px;
|
|
8382
|
+
line-height: 1.35;
|
|
8383
|
+
font-weight: 600;
|
|
8384
|
+
color: #010507;
|
|
8385
|
+
"
|
|
8386
|
+
>
|
|
8387
|
+
${step.title}
|
|
8388
|
+
</div>
|
|
8389
|
+
<div style="font-size: 12px; line-height: 1.5; color: #57575b;">
|
|
8390
|
+
${step.body}
|
|
8391
|
+
</div>
|
|
8392
|
+
<div
|
|
8393
|
+
style="
|
|
8394
|
+
display: flex;
|
|
8395
|
+
justify-content: space-between;
|
|
8396
|
+
gap: 8px;
|
|
8397
|
+
margin-top: 14px;
|
|
8398
|
+
"
|
|
8399
|
+
>
|
|
8400
|
+
<button
|
|
8401
|
+
type="button"
|
|
8402
|
+
style="border:0;background:transparent;color:#838389;font-size:12px;font-weight:600;cursor:pointer;padding:7px 0;"
|
|
8403
|
+
@click=${() => this.dismissExampleTour("skip")}
|
|
8404
|
+
>
|
|
8405
|
+
Skip
|
|
8406
|
+
</button>
|
|
8407
|
+
<div style="display:flex;gap:8px;">
|
|
8408
|
+
<button
|
|
8409
|
+
type="button"
|
|
8410
|
+
style="border:1px solid #dbdbe5;border-radius:6px;background:#ffffff;color:#57575b;font-size:12px;font-weight:600;cursor:pointer;padding:7px 10px;"
|
|
8411
|
+
?disabled=${isFirst}
|
|
8412
|
+
@click=${() => this.setExampleTourStep(this.exampleTourStep - 1)}
|
|
8413
|
+
>
|
|
8414
|
+
Back
|
|
8415
|
+
</button>
|
|
8416
|
+
<button
|
|
8417
|
+
type="button"
|
|
8418
|
+
style="border:1px solid #010507;border-radius:6px;background:#010507;color:#ffffff;font-size:12px;font-weight:600;cursor:pointer;padding:7px 10px;"
|
|
8419
|
+
@click=${() =>
|
|
8420
|
+
isLast
|
|
8421
|
+
? this.dismissExampleTour("done")
|
|
8422
|
+
: this.setExampleTourStep(this.exampleTourStep + 1)}
|
|
8423
|
+
>
|
|
8424
|
+
${isLast ? "Done" : "Next"}
|
|
8425
|
+
</button>
|
|
8426
|
+
</div>
|
|
8427
|
+
</div>
|
|
8428
|
+
</div>
|
|
8429
|
+
`;
|
|
7047
8430
|
}
|
|
7048
8431
|
|
|
7049
8432
|
private renderThreadsLockedBackgroundMockup() {
|
|
@@ -7293,7 +8676,7 @@ ${argsString}</pre
|
|
|
7293
8676
|
"
|
|
7294
8677
|
>
|
|
7295
8678
|
<a
|
|
7296
|
-
href=${this.
|
|
8679
|
+
href=${this.getThreadsIntelligenceSignupUrl()}
|
|
7297
8680
|
target="_blank"
|
|
7298
8681
|
rel="noopener"
|
|
7299
8682
|
style="
|
|
@@ -7303,42 +8686,360 @@ ${argsString}</pre
|
|
|
7303
8686
|
justify-content: center;
|
|
7304
8687
|
gap: 6px;
|
|
7305
8688
|
border-radius: 6px;
|
|
7306
|
-
|
|
8689
|
+
border: 1px solid #dbdbe5;
|
|
8690
|
+
background: #ffffff;
|
|
7307
8691
|
padding: 8px 12px;
|
|
7308
8692
|
font-size: 12px;
|
|
7309
8693
|
font-weight: 600;
|
|
7310
|
-
color: #
|
|
8694
|
+
color: #57575b;
|
|
7311
8695
|
text-decoration: none;
|
|
7312
8696
|
"
|
|
7313
|
-
@click=${this.
|
|
8697
|
+
@click=${this.handleThreadsIntelligenceSignupClick}
|
|
7314
8698
|
>
|
|
7315
|
-
|
|
8699
|
+
Sign up for Intelligence
|
|
7316
8700
|
</a>
|
|
7317
|
-
|
|
7318
|
-
|
|
7319
|
-
|
|
7320
|
-
|
|
8701
|
+
</div>
|
|
8702
|
+
</div>
|
|
8703
|
+
</div>
|
|
8704
|
+
`;
|
|
8705
|
+
}
|
|
8706
|
+
|
|
8707
|
+
/**
|
|
8708
|
+
* Renders the realtime-connection indicator in the memory-store header.
|
|
8709
|
+
* Only `"connected"` shows the live (green-dot) state; `"connecting"` shows a
|
|
8710
|
+
* muted amber "reconnecting" and `"unavailable"` a muted grey "offline", so
|
|
8711
|
+
* the indicator never claims "live" over a frozen snapshot once the realtime
|
|
8712
|
+
* socket has permanently given up.
|
|
8713
|
+
*/
|
|
8714
|
+
private renderMemoryRealtimeIndicator() {
|
|
8715
|
+
const status = this._memoriesRealtimeStatus;
|
|
8716
|
+
const connected = status === "connected";
|
|
8717
|
+
const dotColor = connected
|
|
8718
|
+
? "#22c55e"
|
|
8719
|
+
: status === "connecting"
|
|
8720
|
+
? "#f59e0b"
|
|
8721
|
+
: "#9ca3af";
|
|
8722
|
+
const label =
|
|
8723
|
+
status === "connected"
|
|
8724
|
+
? "live"
|
|
8725
|
+
: status === "connecting"
|
|
8726
|
+
? "reconnecting"
|
|
8727
|
+
: "offline";
|
|
8728
|
+
return html`
|
|
8729
|
+
<span
|
|
8730
|
+
style="
|
|
8731
|
+
display: inline-flex;
|
|
8732
|
+
align-items: center;
|
|
8733
|
+
gap: 4px;
|
|
8734
|
+
font-size: 10px;
|
|
8735
|
+
font-weight: 500;
|
|
8736
|
+
color: ${connected ? "#57575b" : "#838389"};
|
|
8737
|
+
"
|
|
8738
|
+
>
|
|
8739
|
+
<span
|
|
8740
|
+
style="
|
|
8741
|
+
display: inline-block;
|
|
8742
|
+
width: 6px;
|
|
8743
|
+
height: 6px;
|
|
8744
|
+
border-radius: 50%;
|
|
8745
|
+
background: ${dotColor};
|
|
8746
|
+
"
|
|
8747
|
+
></span>
|
|
8748
|
+
${label}
|
|
8749
|
+
</span>
|
|
8750
|
+
`;
|
|
8751
|
+
}
|
|
8752
|
+
|
|
8753
|
+
private renderMemoriesView() {
|
|
8754
|
+
// 1. Locked teaser — intelligence not configured or memories not available.
|
|
8755
|
+
if (!this.core?.intelligence || !this._memoriesAvailable) {
|
|
8756
|
+
return html`
|
|
8757
|
+
<div
|
|
8758
|
+
style="
|
|
8759
|
+
position: relative;
|
|
8760
|
+
height: 100%;
|
|
8761
|
+
display: flex;
|
|
8762
|
+
align-items: center;
|
|
8763
|
+
justify-content: center;
|
|
8764
|
+
padding: 32px;
|
|
8765
|
+
overflow: hidden;
|
|
8766
|
+
background: #ffffff;
|
|
8767
|
+
"
|
|
8768
|
+
>
|
|
8769
|
+
${this.renderThreadsLockedBackgroundMockup()}
|
|
8770
|
+
<div
|
|
8771
|
+
aria-hidden="true"
|
|
8772
|
+
style="
|
|
8773
|
+
position: absolute;
|
|
8774
|
+
inset: 0;
|
|
8775
|
+
pointer-events: none;
|
|
8776
|
+
background:
|
|
8777
|
+
radial-gradient(circle at center, rgba(255,255,255,0.9) 0, rgba(255,255,255,0.78) 24%, rgba(255,255,255,0.34) 48%, rgba(255,255,255,0.56) 100%);
|
|
8778
|
+
"
|
|
8779
|
+
></div>
|
|
8780
|
+
<div
|
|
8781
|
+
style="
|
|
8782
|
+
position: relative;
|
|
8783
|
+
z-index: 1;
|
|
8784
|
+
max-width: 440px;
|
|
8785
|
+
text-align: center;
|
|
8786
|
+
color: #57575b;
|
|
8787
|
+
"
|
|
8788
|
+
>
|
|
8789
|
+
<div
|
|
8790
|
+
aria-hidden="true"
|
|
7321
8791
|
style="
|
|
7322
|
-
|
|
7323
|
-
|
|
7324
|
-
align-items: center;
|
|
8792
|
+
margin: 0 auto 18px;
|
|
8793
|
+
display: flex;
|
|
7325
8794
|
justify-content: center;
|
|
7326
|
-
|
|
7327
|
-
|
|
7328
|
-
|
|
7329
|
-
|
|
7330
|
-
|
|
7331
|
-
|
|
8795
|
+
"
|
|
8796
|
+
>
|
|
8797
|
+
<div
|
|
8798
|
+
style="
|
|
8799
|
+
display: flex;
|
|
8800
|
+
height: 44px;
|
|
8801
|
+
width: 44px;
|
|
8802
|
+
align-items: center;
|
|
8803
|
+
justify-content: center;
|
|
8804
|
+
border: 1px solid #dfd6fb;
|
|
8805
|
+
border-radius: 8px;
|
|
8806
|
+
background: #eee6fe;
|
|
8807
|
+
color: #57575b;
|
|
8808
|
+
box-shadow: 0 8px 18px rgba(87, 87, 91, 0.14);
|
|
8809
|
+
"
|
|
8810
|
+
>
|
|
8811
|
+
${this.renderIcon("Lock")}
|
|
8812
|
+
</div>
|
|
8813
|
+
</div>
|
|
8814
|
+
<h2
|
|
8815
|
+
style="
|
|
8816
|
+
margin: 0 0 8px;
|
|
8817
|
+
font-size: 16px;
|
|
8818
|
+
line-height: 1.35;
|
|
7332
8819
|
font-weight: 600;
|
|
8820
|
+
color: #010507;
|
|
8821
|
+
"
|
|
8822
|
+
>
|
|
8823
|
+
Long-term memory
|
|
8824
|
+
</h2>
|
|
8825
|
+
<p
|
|
8826
|
+
style="
|
|
8827
|
+
margin: 0 auto 18px;
|
|
8828
|
+
max-width: 380px;
|
|
8829
|
+
font-size: 13px;
|
|
8830
|
+
line-height: 1.55;
|
|
7333
8831
|
color: #57575b;
|
|
7334
|
-
text-decoration: none;
|
|
7335
8832
|
"
|
|
7336
|
-
@click=${this.handleThreadsIntelligenceSignupClick}
|
|
7337
8833
|
>
|
|
7338
|
-
|
|
7339
|
-
|
|
8834
|
+
${
|
|
8835
|
+
this._memoryStoreUnsupported
|
|
8836
|
+
? "Long-term memory isn't available in this version of the @copilotkit SDK. Upgrade @copilotkit/core (and @copilotkit/react) to a version that supports memory."
|
|
8837
|
+
: "Long-term memory isn't enabled on this deployment."
|
|
8838
|
+
}
|
|
8839
|
+
</p>
|
|
8840
|
+
<div
|
|
8841
|
+
style="
|
|
8842
|
+
display: flex;
|
|
8843
|
+
flex-wrap: wrap;
|
|
8844
|
+
justify-content: center;
|
|
8845
|
+
gap: 8px;
|
|
8846
|
+
"
|
|
8847
|
+
>
|
|
8848
|
+
<a
|
|
8849
|
+
href=${this.getTalkToEngineerUrl()}
|
|
8850
|
+
target="_blank"
|
|
8851
|
+
rel="noopener"
|
|
8852
|
+
style="
|
|
8853
|
+
display: inline-flex;
|
|
8854
|
+
min-height: 34px;
|
|
8855
|
+
align-items: center;
|
|
8856
|
+
justify-content: center;
|
|
8857
|
+
gap: 6px;
|
|
8858
|
+
border-radius: 6px;
|
|
8859
|
+
background: #010507;
|
|
8860
|
+
padding: 8px 12px;
|
|
8861
|
+
font-size: 12px;
|
|
8862
|
+
font-weight: 600;
|
|
8863
|
+
color: #ffffff;
|
|
8864
|
+
text-decoration: none;
|
|
8865
|
+
"
|
|
8866
|
+
@click=${this.handleThreadsTalkToEngineerClick}
|
|
8867
|
+
>
|
|
8868
|
+
Talk to an Engineer
|
|
8869
|
+
</a>
|
|
8870
|
+
<a
|
|
8871
|
+
href=${this.getIntelligenceSignupUrl()}
|
|
8872
|
+
target="_blank"
|
|
8873
|
+
rel="noopener"
|
|
8874
|
+
style="
|
|
8875
|
+
display: inline-flex;
|
|
8876
|
+
min-height: 34px;
|
|
8877
|
+
align-items: center;
|
|
8878
|
+
justify-content: center;
|
|
8879
|
+
gap: 6px;
|
|
8880
|
+
border-radius: 6px;
|
|
8881
|
+
border: 1px solid #dbdbe5;
|
|
8882
|
+
background: #ffffff;
|
|
8883
|
+
padding: 8px 12px;
|
|
8884
|
+
font-size: 12px;
|
|
8885
|
+
font-weight: 600;
|
|
8886
|
+
color: #57575b;
|
|
8887
|
+
text-decoration: none;
|
|
8888
|
+
"
|
|
8889
|
+
@click=${this.handleThreadsIntelligenceSignupClick}
|
|
8890
|
+
>
|
|
8891
|
+
Sign up for Intelligence
|
|
8892
|
+
</a>
|
|
8893
|
+
</div>
|
|
8894
|
+
</div>
|
|
8895
|
+
</div>
|
|
8896
|
+
`;
|
|
8897
|
+
}
|
|
8898
|
+
|
|
8899
|
+
// 2. Full-screen error — only for a snapshot-LOAD failure (no memories
|
|
8900
|
+
// loaded). A mutation failure that arrives while memories are already on
|
|
8901
|
+
// screen must NOT blank the list; it is surfaced inline below (step 4).
|
|
8902
|
+
if (this._memoriesError && this._memories.length === 0) {
|
|
8903
|
+
return html`
|
|
8904
|
+
<div
|
|
8905
|
+
style="
|
|
8906
|
+
display: flex;
|
|
8907
|
+
height: 100%;
|
|
8908
|
+
flex-direction: column;
|
|
8909
|
+
align-items: center;
|
|
8910
|
+
justify-content: center;
|
|
8911
|
+
gap: 8px;
|
|
8912
|
+
color: #838389;
|
|
8913
|
+
"
|
|
8914
|
+
>
|
|
8915
|
+
<svg
|
|
8916
|
+
width="24"
|
|
8917
|
+
height="24"
|
|
8918
|
+
viewBox="0 0 24 24"
|
|
8919
|
+
fill="none"
|
|
8920
|
+
stroke="#c0333a"
|
|
8921
|
+
stroke-width="1.5"
|
|
8922
|
+
stroke-linecap="round"
|
|
8923
|
+
stroke-linejoin="round"
|
|
8924
|
+
>
|
|
8925
|
+
<circle cx="12" cy="12" r="10" />
|
|
8926
|
+
<line x1="12" y1="8" x2="12" y2="12" />
|
|
8927
|
+
<line x1="12" y1="16" x2="12.01" y2="16" />
|
|
8928
|
+
</svg>
|
|
8929
|
+
<span style="font-size: 13px; color: #c0333a;">
|
|
8930
|
+
Failed to load memories
|
|
8931
|
+
</span>
|
|
8932
|
+
<span
|
|
8933
|
+
style="
|
|
8934
|
+
max-width: 320px;
|
|
8935
|
+
text-align: center;
|
|
8936
|
+
font-size: 11px;
|
|
8937
|
+
line-height: 1.5;
|
|
8938
|
+
color: #c0333a;
|
|
8939
|
+
"
|
|
8940
|
+
>
|
|
8941
|
+
${this._memoriesError.message}
|
|
8942
|
+
</span>
|
|
8943
|
+
</div>
|
|
8944
|
+
`;
|
|
8945
|
+
}
|
|
8946
|
+
|
|
8947
|
+
// 3. Initial loading placeholder (no memories yet to show behind it).
|
|
8948
|
+
if (this._memoriesLoading && this._memories.length === 0) {
|
|
8949
|
+
return html`
|
|
8950
|
+
<div
|
|
8951
|
+
style="
|
|
8952
|
+
display: flex;
|
|
8953
|
+
height: 100%;
|
|
8954
|
+
flex-direction: column;
|
|
8955
|
+
align-items: center;
|
|
8956
|
+
justify-content: center;
|
|
8957
|
+
gap: 8px;
|
|
8958
|
+
color: #838389;
|
|
8959
|
+
"
|
|
8960
|
+
>
|
|
8961
|
+
<svg
|
|
8962
|
+
width="24"
|
|
8963
|
+
height="24"
|
|
8964
|
+
viewBox="0 0 24 24"
|
|
8965
|
+
fill="none"
|
|
8966
|
+
stroke="#c0c0c8"
|
|
8967
|
+
stroke-width="1.5"
|
|
8968
|
+
stroke-linecap="round"
|
|
8969
|
+
stroke-linejoin="round"
|
|
8970
|
+
>
|
|
8971
|
+
<path d="M21 12a9 9 0 1 1-6.219-8.56" />
|
|
8972
|
+
</svg>
|
|
8973
|
+
<span style="font-size: 13px">Loading memories…</span>
|
|
8974
|
+
</div>
|
|
8975
|
+
`;
|
|
8976
|
+
}
|
|
8977
|
+
|
|
8978
|
+
// 4. Content — header + memory list.
|
|
8979
|
+
return html`
|
|
8980
|
+
<div style="display:flex;height:100%;overflow:hidden;flex-direction:column;">
|
|
8981
|
+
<div class="cpk-section-header" style="display:flex;align-items:center;justify-content:space-between;">
|
|
8982
|
+
<h4>Learning</h4>
|
|
8983
|
+
<div style="display:flex;align-items:center;gap:6px;">
|
|
8984
|
+
${this.renderMemoryRealtimeIndicator()}
|
|
8985
|
+
<span
|
|
8986
|
+
style="
|
|
8987
|
+
font-size: 11px;
|
|
8988
|
+
font-weight: 500;
|
|
8989
|
+
color: #57575b;
|
|
8990
|
+
background: rgba(0,0,0,0.07);
|
|
8991
|
+
border-radius: 9999px;
|
|
8992
|
+
padding: 1px 7px;
|
|
8993
|
+
"
|
|
8994
|
+
>
|
|
8995
|
+
${this._memories.length}
|
|
8996
|
+
</span>
|
|
7340
8997
|
</div>
|
|
7341
8998
|
</div>
|
|
8999
|
+
${
|
|
9000
|
+
this._memoriesError
|
|
9001
|
+
? html`
|
|
9002
|
+
<div
|
|
9003
|
+
role="alert"
|
|
9004
|
+
style="
|
|
9005
|
+
display: flex;
|
|
9006
|
+
align-items: flex-start;
|
|
9007
|
+
gap: 8px;
|
|
9008
|
+
flex-shrink: 0;
|
|
9009
|
+
border-bottom: 1px solid #f1c7c9;
|
|
9010
|
+
background: #fdf3f3;
|
|
9011
|
+
padding: 8px 12px;
|
|
9012
|
+
color: #c0333a;
|
|
9013
|
+
font-size: 12px;
|
|
9014
|
+
line-height: 1.45;
|
|
9015
|
+
"
|
|
9016
|
+
>
|
|
9017
|
+
<svg
|
|
9018
|
+
width="16"
|
|
9019
|
+
height="16"
|
|
9020
|
+
viewBox="0 0 24 24"
|
|
9021
|
+
fill="none"
|
|
9022
|
+
stroke="#c0333a"
|
|
9023
|
+
stroke-width="1.5"
|
|
9024
|
+
stroke-linecap="round"
|
|
9025
|
+
stroke-linejoin="round"
|
|
9026
|
+
style="flex-shrink:0;margin-top:1px;"
|
|
9027
|
+
>
|
|
9028
|
+
<circle cx="12" cy="12" r="10" />
|
|
9029
|
+
<line x1="12" y1="8" x2="12" y2="12" />
|
|
9030
|
+
<line x1="12" y1="16" x2="12.01" y2="16" />
|
|
9031
|
+
</svg>
|
|
9032
|
+
<span>Action failed: ${this._memoriesError.message}</span>
|
|
9033
|
+
</div>
|
|
9034
|
+
`
|
|
9035
|
+
: nothing
|
|
9036
|
+
}
|
|
9037
|
+
<div style="flex:1;min-height:0;overflow:hidden;">
|
|
9038
|
+
<cpk-memory-list
|
|
9039
|
+
style="height:100%;"
|
|
9040
|
+
.memories=${this._memories}
|
|
9041
|
+
></cpk-memory-list>
|
|
9042
|
+
</div>
|
|
7342
9043
|
</div>
|
|
7343
9044
|
`;
|
|
7344
9045
|
}
|
|
@@ -7348,28 +9049,26 @@ ${argsString}</pre
|
|
|
7348
9049
|
return this.renderThreadsLockedView();
|
|
7349
9050
|
}
|
|
7350
9051
|
|
|
7351
|
-
const displayThreads =
|
|
7352
|
-
this.
|
|
7353
|
-
? this._threads
|
|
7354
|
-
: (this._threadsByAgent.get(this.selectedContext) ?? []);
|
|
9052
|
+
const { displayThreads, threadsErrorMessage } =
|
|
9053
|
+
this.getActiveThreadsState();
|
|
7355
9054
|
|
|
7356
|
-
|
|
7357
|
-
|
|
7358
|
-
|
|
7359
|
-
|
|
7360
|
-
|
|
7361
|
-
|
|
7362
|
-
|
|
7363
|
-
|
|
7364
|
-
|
|
7365
|
-
|
|
7366
|
-
this._threadsErrorByAgent.get(this.selectedContext)?.message ?? null;
|
|
9055
|
+
const showingExamples = this.shouldRenderExampleThreads(
|
|
9056
|
+
displayThreads,
|
|
9057
|
+
threadsErrorMessage,
|
|
9058
|
+
);
|
|
9059
|
+
const visibleThreads = this.getVisibleThreads(
|
|
9060
|
+
displayThreads,
|
|
9061
|
+
threadsErrorMessage,
|
|
9062
|
+
);
|
|
9063
|
+
if (showingExamples) {
|
|
9064
|
+
this.trackThreadsExampleViewedOnce();
|
|
7367
9065
|
}
|
|
7368
9066
|
|
|
7369
9067
|
const selectedThread =
|
|
7370
9068
|
this.selectedThreadId != null
|
|
7371
|
-
? (
|
|
9069
|
+
? (visibleThreads.find((t) => t.id === this.selectedThreadId) ?? null)
|
|
7372
9070
|
: null;
|
|
9071
|
+
const selectedThreadIsExample = this.isExampleThreadId(selectedThread?.id);
|
|
7373
9072
|
|
|
7374
9073
|
if (!threadsErrorMessage) {
|
|
7375
9074
|
this.trackThreadsViewStateOnce(
|
|
@@ -7380,19 +9079,6 @@ ${argsString}</pre
|
|
|
7380
9079
|
|
|
7381
9080
|
return html`
|
|
7382
9081
|
<div style="display:flex;height:100%;overflow:hidden;flex-direction:column;">
|
|
7383
|
-
<div
|
|
7384
|
-
style="display:flex;align-items:center;justify-content:flex-end;border-bottom:1px solid #DBDBE5;background:#ffffff;padding:8px 12px;flex-shrink:0;"
|
|
7385
|
-
>
|
|
7386
|
-
<a
|
|
7387
|
-
href=${this.getTalkToEngineerUrl()}
|
|
7388
|
-
target="_blank"
|
|
7389
|
-
rel="noopener"
|
|
7390
|
-
style="display:inline-flex;align-items:center;gap:6px;border-radius:6px;border:1px solid #dbdbe5;background:#ffffff;padding:7px 10px;font-size:12px;font-weight:600;color:#57575b;text-decoration:none;"
|
|
7391
|
-
@click=${this.handleTalkToEngineerClick}
|
|
7392
|
-
>
|
|
7393
|
-
Talk to an Engineer
|
|
7394
|
-
</a>
|
|
7395
|
-
</div>
|
|
7396
9082
|
<div style="display:flex;min-height:0;flex:1;overflow:hidden;">
|
|
7397
9083
|
<!-- Left sidebar: thread list -->
|
|
7398
9084
|
<div
|
|
@@ -7400,12 +9086,11 @@ ${argsString}</pre
|
|
|
7400
9086
|
>
|
|
7401
9087
|
<cpk-thread-list
|
|
7402
9088
|
style="height:100%;"
|
|
7403
|
-
.threads=${
|
|
9089
|
+
.threads=${visibleThreads}
|
|
7404
9090
|
.selectedThreadId=${this.selectedThreadId}
|
|
7405
9091
|
.errorMessage=${threadsErrorMessage}
|
|
7406
9092
|
@threadSelected=${(e: CustomEvent<string>) => {
|
|
7407
|
-
this.
|
|
7408
|
-
this.requestUpdate();
|
|
9093
|
+
this.handleThreadsThreadSelected(e.detail, showingExamples);
|
|
7409
9094
|
}}
|
|
7410
9095
|
></cpk-thread-list>
|
|
7411
9096
|
</div>
|
|
@@ -7420,16 +9105,22 @@ ${argsString}</pre
|
|
|
7420
9105
|
></div>
|
|
7421
9106
|
|
|
7422
9107
|
<!-- Center + right: thread details or empty state -->
|
|
7423
|
-
<div style="flex:1;min-width:0;overflow:hidden;display:flex;">
|
|
9108
|
+
<div style="flex:1;min-width:0;overflow:hidden;display:flex;position:relative;">
|
|
7424
9109
|
${
|
|
7425
9110
|
selectedThread
|
|
7426
9111
|
? html`<cpk-thread-details
|
|
7427
9112
|
style="flex:1;min-width:0;"
|
|
7428
9113
|
.threadId=${selectedThread.id}
|
|
7429
9114
|
.thread=${selectedThread}
|
|
9115
|
+
.provider=${
|
|
9116
|
+
selectedThreadIsExample
|
|
9117
|
+
? this.getExampleThreadProvider(selectedThread.id)
|
|
9118
|
+
: null
|
|
9119
|
+
}
|
|
7430
9120
|
.runtimeUrl=${this._core?.runtimeUrl ?? ""}
|
|
7431
9121
|
.headers=${this._core?.headers ?? {}}
|
|
7432
9122
|
.threadInspectionAvailable=${
|
|
9123
|
+
selectedThreadIsExample ||
|
|
7433
9124
|
this._core?.threadEndpoints?.inspect !== false
|
|
7434
9125
|
}
|
|
7435
9126
|
.liveMessageVersion=${
|
|
@@ -7441,8 +9132,11 @@ ${argsString}</pre
|
|
|
7441
9132
|
.agentEventsInput=${
|
|
7442
9133
|
this.agentEvents.get(selectedThread.agentId) ?? []
|
|
7443
9134
|
}
|
|
7444
|
-
></cpk-thread-details
|
|
7445
|
-
|
|
9135
|
+
></cpk-thread-details>
|
|
9136
|
+
${selectedThreadIsExample ? this.renderThreadsExampleTour() : nothing}`
|
|
9137
|
+
: showingExamples
|
|
9138
|
+
? this.renderThreadsExampleOverview()
|
|
9139
|
+
: html`
|
|
7446
9140
|
<div
|
|
7447
9141
|
style="
|
|
7448
9142
|
flex: 1;
|
|
@@ -8162,6 +9856,16 @@ ${prettyEvent}</pre
|
|
|
8162
9856
|
this.autoSelectLatestThread();
|
|
8163
9857
|
}
|
|
8164
9858
|
|
|
9859
|
+
if (key === "memories") {
|
|
9860
|
+
// Lazily create + subscribe to the memory store on first activation. This
|
|
9861
|
+
// is the only place that touches getMemoryStore(), so the store/realtime
|
|
9862
|
+
// are never started just by attaching the inspector.
|
|
9863
|
+
this.ensureMemorySubscription();
|
|
9864
|
+
if (previousMenu !== "memories" && !this.core?.telemetryDisabled) {
|
|
9865
|
+
trackMemoriesTabClicked(this.getMemoriesTelemetryProps());
|
|
9866
|
+
}
|
|
9867
|
+
}
|
|
9868
|
+
|
|
8165
9869
|
if (key === "ag-ui-events" || key === "agents") {
|
|
8166
9870
|
requestAnimationFrame(() => {
|
|
8167
9871
|
const scroller = this.shadowRoot?.getElementById("cpk-main-scroll");
|