@copilotkit/web-inspector 1.64.2-canary.1785346263 → 1.64.2-canary.parallel1
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 +481 -19
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +62 -2
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +62 -2
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +478 -20
- package/dist/index.mjs.map +1 -1
- package/dist/index.umd.js +493 -26
- package/dist/index.umd.js.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__/memory-recall.spec.ts +76 -0
- package/src/__tests__/web-inspector.spec.ts +194 -4
- package/src/index.ts +620 -19
- package/src/styles/generated.css +1 -1
package/src/index.ts
CHANGED
|
@@ -84,16 +84,26 @@ import type {
|
|
|
84
84
|
} from "./lib/telemetry.js";
|
|
85
85
|
|
|
86
86
|
export type { Anchor } from "./lib/types.js";
|
|
87
|
+
export { buildCapabilityRows as ɵbuildCapabilityRows };
|
|
88
|
+
export type { CapabilityToolRow as ɵCapabilityToolRow };
|
|
87
89
|
|
|
88
90
|
export const WEB_INSPECTOR_TAG = "cpk-web-inspector" as const;
|
|
89
91
|
export const THREAD_INSPECTOR_TAG = "cpk-thread-inspector" as const;
|
|
90
92
|
|
|
93
|
+
/**
|
|
94
|
+
* User-facing label for the memory surface (nav item + view header). The menu
|
|
95
|
+
* KEY stays "memories" for persistence/telemetry stability; only the label
|
|
96
|
+
* changed from "Learning" to "Memory".
|
|
97
|
+
*/
|
|
98
|
+
const MEMORY_VIEW_LABEL = "Memory";
|
|
99
|
+
|
|
91
100
|
type LucideIconName = keyof typeof icons;
|
|
92
101
|
|
|
93
102
|
type MenuKey =
|
|
94
103
|
| "ag-ui-events"
|
|
95
104
|
| "agents"
|
|
96
105
|
| "frontend-tools"
|
|
106
|
+
| "capabilities"
|
|
97
107
|
| "agent-context"
|
|
98
108
|
| "threads"
|
|
99
109
|
| "memories"
|
|
@@ -122,6 +132,11 @@ const INTELLIGENCE_SIGNUP_URL = "https://go.copilotkit.ai/intelligence-signup";
|
|
|
122
132
|
const THREADS_INTELLIGENCE_SIGNIN_URL =
|
|
123
133
|
"https://dashboard.operations.copilotkit.ai/sign-in";
|
|
124
134
|
const TALK_TO_ENGINEER_URL = "https://www.copilotkit.ai/talk-to-an-engineer";
|
|
135
|
+
// Label for the Capabilities tab (client-authoritative dev experimentation
|
|
136
|
+
// surface: toggle frontend tools + A2UI catalog components on/off, enforced
|
|
137
|
+
// immediately via core.setToolEnabled / core.setCatalogComponentEnabled).
|
|
138
|
+
// Renameable — keep the display string in this one place.
|
|
139
|
+
const CAPABILITIES_TAB_LABEL = "Capabilities";
|
|
125
140
|
const THREADS_DOCS_URL = "https://docs.copilotkit.ai/threads";
|
|
126
141
|
const SELF_HOSTED_INTELLIGENCE_URL =
|
|
127
142
|
"https://docs.copilotkit.ai/premium/self-hosting";
|
|
@@ -220,6 +235,55 @@ type InspectorToolDefinition = {
|
|
|
220
235
|
type: "handler" | "renderer";
|
|
221
236
|
};
|
|
222
237
|
|
|
238
|
+
// ─── Capabilities tab view-models ────────────────────────────────────────────
|
|
239
|
+
// A single toggle row. `key` is the stable identity used as a Lit list key; for
|
|
240
|
+
// tools it is `${agentId}:${name}` (agentId "" for global tools), for catalog
|
|
241
|
+
// components it is the component name.
|
|
242
|
+
type CapabilityToolRow = {
|
|
243
|
+
key: string;
|
|
244
|
+
name: string;
|
|
245
|
+
description?: string;
|
|
246
|
+
agentId?: string;
|
|
247
|
+
enabled: boolean;
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
// Minimal structural view of CopilotKitCore that the pure helper needs, so
|
|
251
|
+
// buildCapabilityRows is trivially unit-testable with a plain object. Method
|
|
252
|
+
// names MUST match the A1 contract exactly.
|
|
253
|
+
type CapabilityToolSource = {
|
|
254
|
+
tools?: ReadonlyArray<{
|
|
255
|
+
name: string;
|
|
256
|
+
description?: string;
|
|
257
|
+
agentId?: string;
|
|
258
|
+
}>;
|
|
259
|
+
isToolEnabled: (name: string, agentId?: string) => boolean;
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Map core.tools (the registry INCLUDING disabled tools) into Capabilities-tab
|
|
264
|
+
* frontend-tool rows. Pure: no DOM, no `this`. Reads current on/off state from
|
|
265
|
+
* core.isToolEnabled(name, agentId?) per the A1 contract.
|
|
266
|
+
*/
|
|
267
|
+
function buildCapabilityRows(core: CapabilityToolSource): CapabilityToolRow[] {
|
|
268
|
+
const rows: CapabilityToolRow[] = [];
|
|
269
|
+
for (const tool of core.tools ?? []) {
|
|
270
|
+
const agentId = tool.agentId ?? "";
|
|
271
|
+
const key = `${agentId}:${tool.name}`;
|
|
272
|
+
rows.push({
|
|
273
|
+
key,
|
|
274
|
+
name: tool.name,
|
|
275
|
+
description: tool.description,
|
|
276
|
+
agentId: tool.agentId,
|
|
277
|
+
enabled: core.isToolEnabled(tool.name, tool.agentId),
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
return rows.sort((a, b) => {
|
|
281
|
+
const agentCompare = (a.agentId ?? "").localeCompare(b.agentId ?? "");
|
|
282
|
+
if (agentCompare !== 0) return agentCompare;
|
|
283
|
+
return a.name.localeCompare(b.name);
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
|
|
223
287
|
type InspectorEvent = {
|
|
224
288
|
id: string;
|
|
225
289
|
agentId: string;
|
|
@@ -3700,6 +3764,51 @@ ${unsafeHTML(highlightedJson(stateValue))}</pre
|
|
|
3700
3764
|
}
|
|
3701
3765
|
}
|
|
3702
3766
|
|
|
3767
|
+
// ─── memory recall relevance helpers ─────────────────────────────────────────
|
|
3768
|
+
|
|
3769
|
+
/**
|
|
3770
|
+
* Normalizes a memory's raw recall score to a 0..1 relevance ratio relative to
|
|
3771
|
+
* the strongest result in the same result set. Recall scores are RRF scores
|
|
3772
|
+
* (relative), so a bar is only meaningful against the set max. Returns
|
|
3773
|
+
* `undefined` when no meaningful ranking exists (empty set, non-positive max,
|
|
3774
|
+
* missing score) so the caller renders no bar.
|
|
3775
|
+
*/
|
|
3776
|
+
function normalizeRelevance(
|
|
3777
|
+
score: number | undefined,
|
|
3778
|
+
maxScore: number,
|
|
3779
|
+
): number | undefined {
|
|
3780
|
+
if (maxScore <= 0) return undefined;
|
|
3781
|
+
if (score === undefined || !Number.isFinite(score)) return undefined;
|
|
3782
|
+
const ratio = score / maxScore;
|
|
3783
|
+
if (ratio <= 0) return 0;
|
|
3784
|
+
return ratio > 1 ? 1 : ratio;
|
|
3785
|
+
}
|
|
3786
|
+
|
|
3787
|
+
/** Largest finite `score` across a result set, or 0 when none present. */
|
|
3788
|
+
function maxRecallScore(memories: readonly Memory[]): number {
|
|
3789
|
+
let max = 0;
|
|
3790
|
+
for (const m of memories) {
|
|
3791
|
+
const s = m.score;
|
|
3792
|
+
if (typeof s === "number" && Number.isFinite(s) && s > max) max = s;
|
|
3793
|
+
}
|
|
3794
|
+
return max;
|
|
3795
|
+
}
|
|
3796
|
+
|
|
3797
|
+
/**
|
|
3798
|
+
* Percent width for a relevance bar. Mirrors the banking reference
|
|
3799
|
+
* (`max(6, round(rel*100))%`) so a matched-but-weak result still shows a sliver.
|
|
3800
|
+
* Returns a whole number in [6, 100].
|
|
3801
|
+
*/
|
|
3802
|
+
function relevanceBarWidth(relevance: number): number {
|
|
3803
|
+
return Math.max(6, Math.min(100, Math.round(relevance * 100)));
|
|
3804
|
+
}
|
|
3805
|
+
|
|
3806
|
+
export {
|
|
3807
|
+
normalizeRelevance as ɵnormalizeRelevance,
|
|
3808
|
+
maxRecallScore as ɵmaxRecallScore,
|
|
3809
|
+
relevanceBarWidth as ɵrelevanceBarWidth,
|
|
3810
|
+
};
|
|
3811
|
+
|
|
3703
3812
|
// ─── cpk-memory-list ─────────────────────────────────────────────────────────
|
|
3704
3813
|
|
|
3705
3814
|
/** Memory kind values including the "all" sentinel used by the filter UI. */
|
|
@@ -3708,12 +3817,24 @@ type MemoryKindFilter = "all" | "topical" | "episodic" | "operational";
|
|
|
3708
3817
|
class CpkMemoryList extends LitElement {
|
|
3709
3818
|
static properties = {
|
|
3710
3819
|
memories: { attribute: false },
|
|
3820
|
+
recallResults: { attribute: false },
|
|
3821
|
+
recallLoading: { attribute: false },
|
|
3822
|
+
recallError: { attribute: false },
|
|
3823
|
+
recallQueryText: { attribute: false },
|
|
3711
3824
|
search: { state: true },
|
|
3712
3825
|
kind: { state: true },
|
|
3713
3826
|
};
|
|
3714
3827
|
|
|
3715
3828
|
/** Ordered (newest-first) list of memories supplied by the parent. */
|
|
3716
3829
|
memories: Memory[] = [];
|
|
3830
|
+
/** Semantic-recall results. `null` = no recall run (section hidden); `[]` = ran, no matches. */
|
|
3831
|
+
recallResults: Memory[] | null = null;
|
|
3832
|
+
/** True while a recall request is in flight. */
|
|
3833
|
+
recallLoading = false;
|
|
3834
|
+
/** Error message from the most recent recall attempt, or null. */
|
|
3835
|
+
recallError: string | null = null;
|
|
3836
|
+
/** The recall input text (owned by the parent). */
|
|
3837
|
+
recallQueryText = "";
|
|
3717
3838
|
private search = "";
|
|
3718
3839
|
private kind: MemoryKindFilter = "all";
|
|
3719
3840
|
|
|
@@ -3915,6 +4036,117 @@ class CpkMemoryList extends LitElement {
|
|
|
3915
4036
|
.cpk-ml__empty-icon {
|
|
3916
4037
|
color: #c0c0c8;
|
|
3917
4038
|
}
|
|
4039
|
+
|
|
4040
|
+
/* ── Recall ── */
|
|
4041
|
+
.cpk-ml__recall {
|
|
4042
|
+
display: flex;
|
|
4043
|
+
gap: 6px;
|
|
4044
|
+
padding: 10px 12px;
|
|
4045
|
+
border-bottom: 1px solid #dbdbe5;
|
|
4046
|
+
flex-shrink: 0;
|
|
4047
|
+
}
|
|
4048
|
+
.cpk-ml__recall-input {
|
|
4049
|
+
flex: 1;
|
|
4050
|
+
box-sizing: border-box;
|
|
4051
|
+
font-family: "Plus Jakarta Sans", sans-serif;
|
|
4052
|
+
font-size: 12px;
|
|
4053
|
+
padding: 7px 10px;
|
|
4054
|
+
border-radius: 6px;
|
|
4055
|
+
border: 1px solid #dbdbe5;
|
|
4056
|
+
background: #fff;
|
|
4057
|
+
color: #010507;
|
|
4058
|
+
outline: none;
|
|
4059
|
+
transition: border-color 0.15s;
|
|
4060
|
+
}
|
|
4061
|
+
.cpk-ml__recall-input:focus {
|
|
4062
|
+
border-color: #bec2ff;
|
|
4063
|
+
}
|
|
4064
|
+
.cpk-ml__recall-btn {
|
|
4065
|
+
font-family: "Plus Jakarta Sans", sans-serif;
|
|
4066
|
+
font-size: 12px;
|
|
4067
|
+
font-weight: 500;
|
|
4068
|
+
padding: 7px 12px;
|
|
4069
|
+
border-radius: 6px;
|
|
4070
|
+
border: 1px solid #dbdbe5;
|
|
4071
|
+
background: #fff;
|
|
4072
|
+
color: #010507;
|
|
4073
|
+
cursor: pointer;
|
|
4074
|
+
transition: background 0.1s;
|
|
4075
|
+
}
|
|
4076
|
+
.cpk-ml__recall-btn:hover:not(:disabled) {
|
|
4077
|
+
background: #f0f0f5;
|
|
4078
|
+
}
|
|
4079
|
+
.cpk-ml__recall-btn:disabled {
|
|
4080
|
+
opacity: 0.4;
|
|
4081
|
+
cursor: default;
|
|
4082
|
+
}
|
|
4083
|
+
.cpk-ml__recall-section {
|
|
4084
|
+
flex-shrink: 0;
|
|
4085
|
+
max-height: 45%;
|
|
4086
|
+
overflow-y: auto;
|
|
4087
|
+
padding: 8px 12px;
|
|
4088
|
+
border-bottom: 1px solid #dbdbe5;
|
|
4089
|
+
background: #fbfbfd;
|
|
4090
|
+
display: flex;
|
|
4091
|
+
flex-direction: column;
|
|
4092
|
+
gap: 8px;
|
|
4093
|
+
}
|
|
4094
|
+
.cpk-ml__recall-header {
|
|
4095
|
+
display: flex;
|
|
4096
|
+
align-items: center;
|
|
4097
|
+
justify-content: space-between;
|
|
4098
|
+
gap: 8px;
|
|
4099
|
+
}
|
|
4100
|
+
.cpk-ml__recall-title {
|
|
4101
|
+
font-family: "Plus Jakarta Sans", sans-serif;
|
|
4102
|
+
font-size: 12px;
|
|
4103
|
+
font-weight: 600;
|
|
4104
|
+
color: #010507;
|
|
4105
|
+
}
|
|
4106
|
+
.cpk-ml__recall-clear {
|
|
4107
|
+
font-family: "Plus Jakarta Sans", sans-serif;
|
|
4108
|
+
font-size: 10px;
|
|
4109
|
+
color: #838389;
|
|
4110
|
+
background: none;
|
|
4111
|
+
border: none;
|
|
4112
|
+
cursor: pointer;
|
|
4113
|
+
padding: 0;
|
|
4114
|
+
}
|
|
4115
|
+
.cpk-ml__recall-clear:hover {
|
|
4116
|
+
color: #010507;
|
|
4117
|
+
}
|
|
4118
|
+
.cpk-ml__recall-msg {
|
|
4119
|
+
font-size: 11px;
|
|
4120
|
+
color: #838389;
|
|
4121
|
+
line-height: 1.45;
|
|
4122
|
+
}
|
|
4123
|
+
.cpk-ml__recall-msg--error {
|
|
4124
|
+
color: #c0333a;
|
|
4125
|
+
}
|
|
4126
|
+
|
|
4127
|
+
/* ── Relevance bar ── */
|
|
4128
|
+
.cpk-ml__relevance {
|
|
4129
|
+
height: 4px;
|
|
4130
|
+
width: 100%;
|
|
4131
|
+
overflow: hidden;
|
|
4132
|
+
border-radius: 9999px;
|
|
4133
|
+
background: #f0f0f5;
|
|
4134
|
+
}
|
|
4135
|
+
.cpk-ml__relevance-fill {
|
|
4136
|
+
height: 100%;
|
|
4137
|
+
border-radius: 9999px;
|
|
4138
|
+
background: #6366f1;
|
|
4139
|
+
}
|
|
4140
|
+
|
|
4141
|
+
/* ── Scope badge variants ── */
|
|
4142
|
+
.cpk-ml__scope-badge--user {
|
|
4143
|
+
background: #f0f0f5;
|
|
4144
|
+
color: #838389;
|
|
4145
|
+
}
|
|
4146
|
+
.cpk-ml__scope-badge--project {
|
|
4147
|
+
background: #fef3c7;
|
|
4148
|
+
color: #92660c;
|
|
4149
|
+
}
|
|
3918
4150
|
`;
|
|
3919
4151
|
|
|
3920
4152
|
/** Memories that pass the current text search (before kind filter). */
|
|
@@ -3959,6 +4191,135 @@ class CpkMemoryList extends LitElement {
|
|
|
3959
4191
|
>`;
|
|
3960
4192
|
}
|
|
3961
4193
|
|
|
4194
|
+
private renderScopeBadge(scope: string): TemplateResult {
|
|
4195
|
+
const variant = scope === "project" ? "project" : "user";
|
|
4196
|
+
return html`<span
|
|
4197
|
+
class="cpk-ml__scope-badge cpk-ml__scope-badge--${variant}"
|
|
4198
|
+
>${scope}</span
|
|
4199
|
+
>`;
|
|
4200
|
+
}
|
|
4201
|
+
|
|
4202
|
+
/**
|
|
4203
|
+
* Renders one memory card. `relevance` (0..1) is supplied only for recall
|
|
4204
|
+
* results — when present a relevance bar is drawn; the full list omits it.
|
|
4205
|
+
*/
|
|
4206
|
+
private renderCard(m: Memory, relevance?: number): TemplateResult {
|
|
4207
|
+
const threads = m.sourceThreadIds.length;
|
|
4208
|
+
return html`
|
|
4209
|
+
<div class="cpk-ml__card">
|
|
4210
|
+
<div class="cpk-ml__card-badges">
|
|
4211
|
+
${this.renderKindBadge(m.kind)}${this.renderScopeBadge(m.scope)}
|
|
4212
|
+
</div>
|
|
4213
|
+
<div class="cpk-ml__content">${m.content}</div>
|
|
4214
|
+
${
|
|
4215
|
+
relevance !== undefined
|
|
4216
|
+
? html`<div class="cpk-ml__relevance">
|
|
4217
|
+
<div
|
|
4218
|
+
class="cpk-ml__relevance-fill"
|
|
4219
|
+
style="width:${relevanceBarWidth(relevance)}%;"
|
|
4220
|
+
></div>
|
|
4221
|
+
</div>`
|
|
4222
|
+
: nothing
|
|
4223
|
+
}
|
|
4224
|
+
<div class="cpk-ml__footer">
|
|
4225
|
+
<span class="cpk-ml__footer-threads"
|
|
4226
|
+
>${threads} source thread${threads === 1 ? "" : "s"}</span
|
|
4227
|
+
>
|
|
4228
|
+
<span class="cpk-ml__footer-id">${this.shortId(m.id)}</span>
|
|
4229
|
+
</div>
|
|
4230
|
+
</div>
|
|
4231
|
+
`;
|
|
4232
|
+
}
|
|
4233
|
+
|
|
4234
|
+
private onRecallInput = (event: Event): void => {
|
|
4235
|
+
const value = (event.target as HTMLInputElement).value;
|
|
4236
|
+
this.recallQueryText = value;
|
|
4237
|
+
this.dispatchEvent(
|
|
4238
|
+
new CustomEvent<string>("recallQueryChanged", {
|
|
4239
|
+
detail: value,
|
|
4240
|
+
bubbles: true,
|
|
4241
|
+
composed: true,
|
|
4242
|
+
}),
|
|
4243
|
+
);
|
|
4244
|
+
};
|
|
4245
|
+
|
|
4246
|
+
private onRecallSubmit = (event: Event): void => {
|
|
4247
|
+
event.preventDefault();
|
|
4248
|
+
const query = this.recallQueryText.trim();
|
|
4249
|
+
if (query.length === 0 || this.recallLoading) return;
|
|
4250
|
+
this.dispatchEvent(
|
|
4251
|
+
new CustomEvent<string>("recallSubmitted", {
|
|
4252
|
+
detail: query,
|
|
4253
|
+
bubbles: true,
|
|
4254
|
+
composed: true,
|
|
4255
|
+
}),
|
|
4256
|
+
);
|
|
4257
|
+
};
|
|
4258
|
+
|
|
4259
|
+
private onRecallClear = (): void => {
|
|
4260
|
+
this.dispatchEvent(
|
|
4261
|
+
new CustomEvent("recallCleared", { bubbles: true, composed: true }),
|
|
4262
|
+
);
|
|
4263
|
+
};
|
|
4264
|
+
|
|
4265
|
+
private renderRecallForm(): TemplateResult {
|
|
4266
|
+
const disabled =
|
|
4267
|
+
this.recallLoading || this.recallQueryText.trim().length === 0;
|
|
4268
|
+
return html`
|
|
4269
|
+
<form class="cpk-ml__recall" @submit=${this.onRecallSubmit}>
|
|
4270
|
+
<input
|
|
4271
|
+
type="text"
|
|
4272
|
+
placeholder="Recall by meaning…"
|
|
4273
|
+
aria-label="Recall memories by meaning"
|
|
4274
|
+
class="cpk-ml__recall-input"
|
|
4275
|
+
.value=${this.recallQueryText}
|
|
4276
|
+
@input=${this.onRecallInput}
|
|
4277
|
+
/>
|
|
4278
|
+
<button type="submit" class="cpk-ml__recall-btn" ?disabled=${disabled}>
|
|
4279
|
+
${this.recallLoading ? "…" : "Recall"}
|
|
4280
|
+
</button>
|
|
4281
|
+
</form>
|
|
4282
|
+
`;
|
|
4283
|
+
}
|
|
4284
|
+
|
|
4285
|
+
private renderRecallSection(): TemplateResult {
|
|
4286
|
+
const results = this.recallResults;
|
|
4287
|
+
if (results === null) return html``;
|
|
4288
|
+
const max = maxRecallScore(results);
|
|
4289
|
+
return html`
|
|
4290
|
+
<section
|
|
4291
|
+
class="cpk-ml__recall-section"
|
|
4292
|
+
aria-label="Semantic recall results"
|
|
4293
|
+
>
|
|
4294
|
+
<div class="cpk-ml__recall-header">
|
|
4295
|
+
<span class="cpk-ml__recall-title"
|
|
4296
|
+
>Semantic recall (${results.length})</span
|
|
4297
|
+
>
|
|
4298
|
+
<button
|
|
4299
|
+
type="button"
|
|
4300
|
+
class="cpk-ml__recall-clear"
|
|
4301
|
+
@click=${this.onRecallClear}
|
|
4302
|
+
>
|
|
4303
|
+
Clear
|
|
4304
|
+
</button>
|
|
4305
|
+
</div>
|
|
4306
|
+
${
|
|
4307
|
+
this.recallError
|
|
4308
|
+
? html`<p class="cpk-ml__recall-msg cpk-ml__recall-msg--error">
|
|
4309
|
+
Recall failed: ${this.recallError}
|
|
4310
|
+
</p>`
|
|
4311
|
+
: results.length === 0
|
|
4312
|
+
? html`
|
|
4313
|
+
<p class="cpk-ml__recall-msg">No memories matched that query.</p>
|
|
4314
|
+
`
|
|
4315
|
+
: results.map((m) =>
|
|
4316
|
+
this.renderCard(m, normalizeRelevance(m.score, max)),
|
|
4317
|
+
)
|
|
4318
|
+
}
|
|
4319
|
+
</section>
|
|
4320
|
+
`;
|
|
4321
|
+
}
|
|
4322
|
+
|
|
3962
4323
|
private renderEmpty(): TemplateResult {
|
|
3963
4324
|
const q = this.search.trim();
|
|
3964
4325
|
if (this.memories.length === 0) {
|
|
@@ -4005,6 +4366,9 @@ class CpkMemoryList extends LitElement {
|
|
|
4005
4366
|
|
|
4006
4367
|
return html`
|
|
4007
4368
|
<div class="cpk-ml">
|
|
4369
|
+
<!-- Semantic recall -->
|
|
4370
|
+
${this.renderRecallForm()} ${this.renderRecallSection()}
|
|
4371
|
+
|
|
4008
4372
|
<!-- Search -->
|
|
4009
4373
|
<div class="cpk-ml__search">
|
|
4010
4374
|
<input
|
|
@@ -4038,23 +4402,7 @@ class CpkMemoryList extends LitElement {
|
|
|
4038
4402
|
|
|
4039
4403
|
<!-- Memory list -->
|
|
4040
4404
|
<div class="cpk-ml__list">
|
|
4041
|
-
${filtered.map(
|
|
4042
|
-
(m) => html`
|
|
4043
|
-
<div class="cpk-ml__card">
|
|
4044
|
-
<div class="cpk-ml__card-badges">
|
|
4045
|
-
${this.renderKindBadge(m.kind)}
|
|
4046
|
-
<span class="cpk-ml__scope-badge">${m.scope}</span>
|
|
4047
|
-
</div>
|
|
4048
|
-
<div class="cpk-ml__content">${m.content}</div>
|
|
4049
|
-
<div class="cpk-ml__footer">
|
|
4050
|
-
<span class="cpk-ml__footer-threads"
|
|
4051
|
-
>${m.sourceThreadIds.length} source thread${m.sourceThreadIds.length === 1 ? "" : "s"}</span
|
|
4052
|
-
>
|
|
4053
|
-
<span class="cpk-ml__footer-id">${this.shortId(m.id)}</span>
|
|
4054
|
-
</div>
|
|
4055
|
-
</div>
|
|
4056
|
-
`,
|
|
4057
|
-
)}
|
|
4405
|
+
${filtered.map((m) => this.renderCard(m))}
|
|
4058
4406
|
${filtered.length === 0 ? this.renderEmpty() : nothing}
|
|
4059
4407
|
</div>
|
|
4060
4408
|
</div>
|
|
@@ -4084,6 +4432,7 @@ export class WebInspectorElement extends LitElement {
|
|
|
4084
4432
|
static properties = {
|
|
4085
4433
|
core: { attribute: false },
|
|
4086
4434
|
autoAttachCore: { type: Boolean, attribute: "auto-attach-core" },
|
|
4435
|
+
_capabilitiesVersion: { state: true },
|
|
4087
4436
|
} as const;
|
|
4088
4437
|
|
|
4089
4438
|
private _core: CopilotKitCore | null = null;
|
|
@@ -4108,6 +4457,15 @@ export class WebInspectorElement extends LitElement {
|
|
|
4108
4457
|
// SDK). Distinct from `_memoriesAvailable` (memory not enabled on an
|
|
4109
4458
|
// otherwise-current deployment) so the teaser can show upgrade-the-SDK copy.
|
|
4110
4459
|
private _memoryStoreUnsupported = false;
|
|
4460
|
+
// ── Semantic recall (B3) ──────────────────────────────────────────────
|
|
4461
|
+
// `null` = no recall run yet (section hidden). `[]` = ran, no matches.
|
|
4462
|
+
private _recallResults: Memory[] | null = null;
|
|
4463
|
+
private _recallLoading = false;
|
|
4464
|
+
private _recallError: string | null = null;
|
|
4465
|
+
private _recallQuery = "";
|
|
4466
|
+
// Monotonic token so a slow recall resolving after a newer one / Clear /
|
|
4467
|
+
// detach is ignored — last-write-wins without racing state.
|
|
4468
|
+
private _recallSeq = 0;
|
|
4111
4469
|
private runtimeStatus: CopilotKitCoreRuntimeConnectionStatus | null = null;
|
|
4112
4470
|
private coreProperties: Readonly<Record<string, unknown>> = {};
|
|
4113
4471
|
private lastCoreError: {
|
|
@@ -4167,6 +4525,12 @@ export class WebInspectorElement extends LitElement {
|
|
|
4167
4525
|
private attemptedAutoAttach = false;
|
|
4168
4526
|
private cachedTools: InspectorToolDefinition[] = [];
|
|
4169
4527
|
private toolSignature = "";
|
|
4528
|
+
// Bumped after every core.setToolEnabled / core.setCatalogComponentEnabled
|
|
4529
|
+
// call so the Capabilities tab re-paints from the fresh isToolEnabled /
|
|
4530
|
+
// isCatalogComponentEnabled getters. There is no core subscriber for
|
|
4531
|
+
// enablement changes — the inspector itself drives the toggle, so we force
|
|
4532
|
+
// the re-render locally.
|
|
4533
|
+
private _capabilitiesVersion = 0;
|
|
4170
4534
|
private eventFilterText = "";
|
|
4171
4535
|
private eventTypeFilter: InspectorAgentEventType | "all" = "all";
|
|
4172
4536
|
// Column widths for the AG-UI events table (agent, time, event-type; last col is auto)
|
|
@@ -4272,6 +4636,8 @@ export class WebInspectorElement extends LitElement {
|
|
|
4272
4636
|
|
|
4273
4637
|
private get menuItems(): MenuItem[] {
|
|
4274
4638
|
const hasFrontendTools = (this._core?.tools?.length ?? 0) > 0;
|
|
4639
|
+
const hasCatalog = (this._core?.catalogComponents?.length ?? 0) > 0;
|
|
4640
|
+
const hasCapabilities = hasFrontendTools || hasCatalog;
|
|
4275
4641
|
return [
|
|
4276
4642
|
{
|
|
4277
4643
|
key: "ag-ui-events",
|
|
@@ -4288,6 +4654,15 @@ export class WebInspectorElement extends LitElement {
|
|
|
4288
4654
|
},
|
|
4289
4655
|
]
|
|
4290
4656
|
: []),
|
|
4657
|
+
...(hasCapabilities
|
|
4658
|
+
? [
|
|
4659
|
+
{
|
|
4660
|
+
key: "capabilities" as const,
|
|
4661
|
+
label: CAPABILITIES_TAB_LABEL,
|
|
4662
|
+
icon: "SlidersHorizontal" as LucideIconName,
|
|
4663
|
+
},
|
|
4664
|
+
]
|
|
4665
|
+
: []),
|
|
4291
4666
|
{
|
|
4292
4667
|
key: "agent-context",
|
|
4293
4668
|
label: "Context",
|
|
@@ -4298,7 +4673,11 @@ export class WebInspectorElement extends LitElement {
|
|
|
4298
4673
|
label: "Threads",
|
|
4299
4674
|
icon: "MessageSquare" as LucideIconName,
|
|
4300
4675
|
},
|
|
4301
|
-
{
|
|
4676
|
+
{
|
|
4677
|
+
key: "memories",
|
|
4678
|
+
label: MEMORY_VIEW_LABEL,
|
|
4679
|
+
icon: "Brain" as LucideIconName,
|
|
4680
|
+
},
|
|
4302
4681
|
];
|
|
4303
4682
|
}
|
|
4304
4683
|
|
|
@@ -4712,6 +5091,58 @@ export class WebInspectorElement extends LitElement {
|
|
|
4712
5091
|
this.requestUpdate();
|
|
4713
5092
|
}
|
|
4714
5093
|
|
|
5094
|
+
/**
|
|
5095
|
+
* Runs a semantic recall via the memory store (`core.getMemoryStore().recall`,
|
|
5096
|
+
* from B2) and stores ranked results. Guarded by a monotonic sequence token
|
|
5097
|
+
* so a stale request cannot overwrite a newer result / Clear / detach. Only
|
|
5098
|
+
* reachable from the Intelligence-gated memory view, so it inherits the gate.
|
|
5099
|
+
*/
|
|
5100
|
+
private runRecall(query: string): void {
|
|
5101
|
+
const trimmed = query.trim();
|
|
5102
|
+
if (trimmed.length === 0) return;
|
|
5103
|
+
const store = this._core?.getMemoryStore?.();
|
|
5104
|
+
if (!store || typeof store.recall !== "function") {
|
|
5105
|
+
this._recallResults = [];
|
|
5106
|
+
this._recallError = "Recall is not supported by this SDK version.";
|
|
5107
|
+
this._recallLoading = false;
|
|
5108
|
+
this.requestUpdate();
|
|
5109
|
+
return;
|
|
5110
|
+
}
|
|
5111
|
+
|
|
5112
|
+
const seq = ++this._recallSeq;
|
|
5113
|
+
this._recallLoading = true;
|
|
5114
|
+
this._recallError = null;
|
|
5115
|
+
this.requestUpdate();
|
|
5116
|
+
|
|
5117
|
+
store
|
|
5118
|
+
.recall(trimmed)
|
|
5119
|
+
.then((results) => {
|
|
5120
|
+
if (seq !== this._recallSeq) return;
|
|
5121
|
+
this._recallResults = results;
|
|
5122
|
+
this._recallError = null;
|
|
5123
|
+
this._recallLoading = false;
|
|
5124
|
+
this.requestUpdate();
|
|
5125
|
+
})
|
|
5126
|
+
.catch((error: unknown) => {
|
|
5127
|
+
if (seq !== this._recallSeq) return;
|
|
5128
|
+
this._recallResults = [];
|
|
5129
|
+
this._recallError =
|
|
5130
|
+
error instanceof Error ? error.message : "unknown error";
|
|
5131
|
+
this._recallLoading = false;
|
|
5132
|
+
this.requestUpdate();
|
|
5133
|
+
});
|
|
5134
|
+
}
|
|
5135
|
+
|
|
5136
|
+
/** Clears recall results/section and cancels any in-flight recall. */
|
|
5137
|
+
private clearRecall(): void {
|
|
5138
|
+
this._recallSeq += 1;
|
|
5139
|
+
this._recallResults = null;
|
|
5140
|
+
this._recallError = null;
|
|
5141
|
+
this._recallLoading = false;
|
|
5142
|
+
this._recallQuery = "";
|
|
5143
|
+
this.requestUpdate();
|
|
5144
|
+
}
|
|
5145
|
+
|
|
4715
5146
|
private detachFromCore(): void {
|
|
4716
5147
|
if (this.coreUnsubscribe) {
|
|
4717
5148
|
this.coreUnsubscribe();
|
|
@@ -4728,6 +5159,13 @@ export class WebInspectorElement extends LitElement {
|
|
|
4728
5159
|
// activation re-subscribes (and re-evaluates SDK support) cleanly.
|
|
4729
5160
|
this._memorySubscribed = false;
|
|
4730
5161
|
this._memoryStoreUnsupported = false;
|
|
5162
|
+
// Reset recall state and bump the sequence token so any in-flight recall
|
|
5163
|
+
// resolving after detach is ignored.
|
|
5164
|
+
this._recallSeq += 1;
|
|
5165
|
+
this._recallResults = null;
|
|
5166
|
+
this._recallLoading = false;
|
|
5167
|
+
this._recallError = null;
|
|
5168
|
+
this._recallQuery = "";
|
|
4731
5169
|
this.coreSubscriber = null;
|
|
4732
5170
|
this.runtimeStatus = null;
|
|
4733
5171
|
this.lastCoreError = null;
|
|
@@ -7791,6 +8229,10 @@ ${argsString}</pre
|
|
|
7791
8229
|
return this.renderToolsView();
|
|
7792
8230
|
}
|
|
7793
8231
|
|
|
8232
|
+
if (this.selectedMenu === "capabilities") {
|
|
8233
|
+
return this.renderCapabilitiesView();
|
|
8234
|
+
}
|
|
8235
|
+
|
|
7794
8236
|
if (this.selectedMenu === "agent-context") {
|
|
7795
8237
|
return this.renderContextView();
|
|
7796
8238
|
}
|
|
@@ -8987,7 +9429,7 @@ ${argsString}</pre
|
|
|
8987
9429
|
return html`
|
|
8988
9430
|
<div style="display:flex;height:100%;overflow:hidden;flex-direction:column;">
|
|
8989
9431
|
<div class="cpk-section-header" style="display:flex;align-items:center;justify-content:space-between;">
|
|
8990
|
-
<h4
|
|
9432
|
+
<h4>${MEMORY_VIEW_LABEL}</h4>
|
|
8991
9433
|
<div style="display:flex;align-items:center;gap:6px;">
|
|
8992
9434
|
${this.renderMemoryRealtimeIndicator()}
|
|
8993
9435
|
<span
|
|
@@ -9046,6 +9488,19 @@ ${argsString}</pre
|
|
|
9046
9488
|
<cpk-memory-list
|
|
9047
9489
|
style="height:100%;"
|
|
9048
9490
|
.memories=${this._memories}
|
|
9491
|
+
.recallResults=${this._recallResults}
|
|
9492
|
+
.recallLoading=${this._recallLoading}
|
|
9493
|
+
.recallError=${this._recallError}
|
|
9494
|
+
.recallQueryText=${this._recallQuery}
|
|
9495
|
+
@recallQueryChanged=${(e: CustomEvent<string>) => {
|
|
9496
|
+
this._recallQuery = e.detail;
|
|
9497
|
+
}}
|
|
9498
|
+
@recallSubmitted=${(e: CustomEvent<string>) => {
|
|
9499
|
+
this.runRecall(e.detail);
|
|
9500
|
+
}}
|
|
9501
|
+
@recallCleared=${() => {
|
|
9502
|
+
this.clearRecall();
|
|
9503
|
+
}}
|
|
9049
9504
|
></cpk-memory-list>
|
|
9050
9505
|
</div>
|
|
9051
9506
|
</div>
|
|
@@ -9908,6 +10363,152 @@ ${prettyEvent}</pre
|
|
|
9908
10363
|
this.requestUpdate();
|
|
9909
10364
|
}
|
|
9910
10365
|
|
|
10366
|
+
private renderCapabilitiesView() {
|
|
10367
|
+
if (!this._core) {
|
|
10368
|
+
return html`
|
|
10369
|
+
<div
|
|
10370
|
+
class="flex h-full items-center justify-center px-4 py-8 text-xs text-gray-500"
|
|
10371
|
+
>
|
|
10372
|
+
No core instance available
|
|
10373
|
+
</div>
|
|
10374
|
+
`;
|
|
10375
|
+
}
|
|
10376
|
+
|
|
10377
|
+
const toolRows = buildCapabilityRows(
|
|
10378
|
+
this._core as unknown as CapabilityToolSource,
|
|
10379
|
+
);
|
|
10380
|
+
const catalog = this._core.catalogComponents ?? [];
|
|
10381
|
+
const hasCatalog = catalog.length > 0;
|
|
10382
|
+
|
|
10383
|
+
if (toolRows.length === 0 && !hasCatalog) {
|
|
10384
|
+
return html`
|
|
10385
|
+
<div class="flex h-full items-center justify-center px-4 py-8 text-center">
|
|
10386
|
+
<div class="max-w-md">
|
|
10387
|
+
<div class="mb-3 flex justify-center text-gray-300 [&>svg]:!h-8 [&>svg]:!w-8">
|
|
10388
|
+
${this.renderIcon("SlidersHorizontal")}
|
|
10389
|
+
</div>
|
|
10390
|
+
<p class="text-sm text-gray-600">No capabilities registered</p>
|
|
10391
|
+
<p class="mt-2 text-xs text-gray-500">
|
|
10392
|
+
Frontend tools and A2UI catalog components will appear here once
|
|
10393
|
+
they are registered on the CopilotKit core.
|
|
10394
|
+
</p>
|
|
10395
|
+
</div>
|
|
10396
|
+
</div>
|
|
10397
|
+
`;
|
|
10398
|
+
}
|
|
10399
|
+
|
|
10400
|
+
return html`
|
|
10401
|
+
<div class="flex h-full flex-col overflow-hidden">
|
|
10402
|
+
<div class="overflow-auto p-4">
|
|
10403
|
+
<div class="space-y-3">
|
|
10404
|
+
<p class="text-xs text-gray-500">
|
|
10405
|
+
Toggle a capability off to omit it from what the agent sees. This
|
|
10406
|
+
is a client-side experimentation surface and takes effect
|
|
10407
|
+
immediately.
|
|
10408
|
+
</p>
|
|
10409
|
+
</div>
|
|
10410
|
+
|
|
10411
|
+
${
|
|
10412
|
+
toolRows.length > 0
|
|
10413
|
+
? html`
|
|
10414
|
+
<div class="mt-4 space-y-2">
|
|
10415
|
+
<h3 class="text-sm text-slate-500">Frontend tools</h3>
|
|
10416
|
+
<div class="space-y-2">
|
|
10417
|
+
${toolRows.map((row) => this.renderCapabilityRow(row))}
|
|
10418
|
+
</div>
|
|
10419
|
+
</div>
|
|
10420
|
+
`
|
|
10421
|
+
: nothing
|
|
10422
|
+
}
|
|
10423
|
+
|
|
10424
|
+
${
|
|
10425
|
+
hasCatalog
|
|
10426
|
+
? html`
|
|
10427
|
+
<div class="mt-6 space-y-2">
|
|
10428
|
+
<h3 class="text-sm text-slate-500">A2UI catalog components</h3>
|
|
10429
|
+
<div class="space-y-2">
|
|
10430
|
+
${catalog.map((component) =>
|
|
10431
|
+
this.renderCapabilityRow({
|
|
10432
|
+
key: component.name,
|
|
10433
|
+
name: component.name,
|
|
10434
|
+
description: component.description,
|
|
10435
|
+
enabled: this._core!.isCatalogComponentEnabled(
|
|
10436
|
+
component.name,
|
|
10437
|
+
),
|
|
10438
|
+
}),
|
|
10439
|
+
)}
|
|
10440
|
+
</div>
|
|
10441
|
+
</div>
|
|
10442
|
+
`
|
|
10443
|
+
: nothing
|
|
10444
|
+
}
|
|
10445
|
+
</div>
|
|
10446
|
+
</div>
|
|
10447
|
+
`;
|
|
10448
|
+
}
|
|
10449
|
+
|
|
10450
|
+
private renderCapabilityRow(row: CapabilityToolRow) {
|
|
10451
|
+
// Frontend-tool keys are always `${agentId}:${name}` (agentId may be ""),
|
|
10452
|
+
// so they contain a ":"; catalog keys are the bare component name.
|
|
10453
|
+
const isTool = row.key.includes(":");
|
|
10454
|
+
return html`
|
|
10455
|
+
<div class="flex items-center justify-between gap-3 rounded-lg border border-gray-200 bg-white px-4 py-3">
|
|
10456
|
+
<div class="min-w-0 flex-1">
|
|
10457
|
+
<div class="flex items-center gap-2">
|
|
10458
|
+
<span class="font-mono text-sm font-semibold text-gray-900">${row.name}</span>
|
|
10459
|
+
${
|
|
10460
|
+
row.agentId
|
|
10461
|
+
? html`<span class="inline-flex items-center gap-1 text-xs text-gray-500">
|
|
10462
|
+
${this.renderIcon("Bot")}<span class="font-mono">${row.agentId}</span>
|
|
10463
|
+
</span>`
|
|
10464
|
+
: nothing
|
|
10465
|
+
}
|
|
10466
|
+
</div>
|
|
10467
|
+
${row.description ? html`<p class="mt-1 text-xs text-gray-600">${row.description}</p>` : nothing}
|
|
10468
|
+
</div>
|
|
10469
|
+
${this.renderCapabilitySwitch(row.enabled, () =>
|
|
10470
|
+
isTool
|
|
10471
|
+
? this.handleToggleTool(row)
|
|
10472
|
+
: this.handleToggleCatalogComponent(row.name),
|
|
10473
|
+
)}
|
|
10474
|
+
</div>
|
|
10475
|
+
`;
|
|
10476
|
+
}
|
|
10477
|
+
|
|
10478
|
+
private renderCapabilitySwitch(enabled: boolean, onToggle: () => void) {
|
|
10479
|
+
const track = enabled ? "bg-emerald-500" : "bg-gray-300";
|
|
10480
|
+
const knob = enabled ? "translate-x-4" : "translate-x-0.5";
|
|
10481
|
+
return html`
|
|
10482
|
+
<button
|
|
10483
|
+
type="button"
|
|
10484
|
+
role="switch"
|
|
10485
|
+
aria-checked=${enabled ? "true" : "false"}
|
|
10486
|
+
class="relative inline-flex h-5 w-9 shrink-0 items-center rounded-full transition-colors focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-gray-300 ${track}"
|
|
10487
|
+
@click=${onToggle}
|
|
10488
|
+
>
|
|
10489
|
+
<span class="inline-block h-4 w-4 transform rounded-full bg-white shadow transition-transform ${knob}"></span>
|
|
10490
|
+
</button>
|
|
10491
|
+
`;
|
|
10492
|
+
}
|
|
10493
|
+
|
|
10494
|
+
private handleToggleTool(row: CapabilityToolRow): void {
|
|
10495
|
+
if (!this._core) return;
|
|
10496
|
+
const next = !row.enabled;
|
|
10497
|
+
// A1 contract: setToolEnabled(name, enabled, agentId?). Pass agentId only
|
|
10498
|
+
// when the tool is agent-scoped so global tools toggle globally.
|
|
10499
|
+
this._core.setToolEnabled(row.name, next, row.agentId);
|
|
10500
|
+
this._capabilitiesVersion += 1;
|
|
10501
|
+
this.requestUpdate();
|
|
10502
|
+
}
|
|
10503
|
+
|
|
10504
|
+
private handleToggleCatalogComponent(name: string): void {
|
|
10505
|
+
if (!this._core) return;
|
|
10506
|
+
const next = !this._core.isCatalogComponentEnabled(name);
|
|
10507
|
+
this._core.setCatalogComponentEnabled(name, next);
|
|
10508
|
+
this._capabilitiesVersion += 1;
|
|
10509
|
+
this.requestUpdate();
|
|
10510
|
+
}
|
|
10511
|
+
|
|
9911
10512
|
private renderToolsView() {
|
|
9912
10513
|
if (!this._core) {
|
|
9913
10514
|
return html`
|