@copilotkit/shared 1.66.2 → 1.67.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.
Files changed (40) hide show
  1. package/README.md +53 -0
  2. package/dist/index.cjs +2 -0
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.d.cts +2 -1
  5. package/dist/index.d.cts.map +1 -1
  6. package/dist/index.d.mts +2 -1
  7. package/dist/index.d.mts.map +1 -1
  8. package/dist/index.mjs +2 -1
  9. package/dist/index.mjs.map +1 -1
  10. package/dist/index.umd.js +149 -4
  11. package/dist/index.umd.js.map +1 -1
  12. package/dist/package.cjs +1 -1
  13. package/dist/package.mjs +1 -1
  14. package/dist/utils/index.cjs +3 -2
  15. package/dist/utils/index.cjs.map +1 -1
  16. package/dist/utils/index.d.cts +1 -0
  17. package/dist/utils/index.d.cts.map +1 -1
  18. package/dist/utils/index.d.mts +1 -0
  19. package/dist/utils/index.d.mts.map +1 -1
  20. package/dist/utils/index.mjs +3 -2
  21. package/dist/utils/index.mjs.map +1 -1
  22. package/dist/utils/inspector-metadata.cjs +147 -0
  23. package/dist/utils/inspector-metadata.cjs.map +1 -0
  24. package/dist/utils/inspector-metadata.d.cts +55 -0
  25. package/dist/utils/inspector-metadata.d.cts.map +1 -0
  26. package/dist/utils/inspector-metadata.d.mts +55 -0
  27. package/dist/utils/inspector-metadata.d.mts.map +1 -0
  28. package/dist/utils/inspector-metadata.mjs +146 -0
  29. package/dist/utils/inspector-metadata.mjs.map +1 -0
  30. package/dist/utils/types.cjs.map +1 -1
  31. package/dist/utils/types.d.cts +2 -0
  32. package/dist/utils/types.d.cts.map +1 -1
  33. package/dist/utils/types.d.mts +2 -0
  34. package/dist/utils/types.d.mts.map +1 -1
  35. package/dist/utils/types.mjs.map +1 -1
  36. package/package.json +1 -1
  37. package/src/utils/index.ts +3 -2
  38. package/src/utils/inspector-metadata.test.ts +742 -0
  39. package/src/utils/inspector-metadata.ts +273 -0
  40. package/src/utils/types.ts +2 -0
@@ -0,0 +1,273 @@
1
+ /**
2
+ * Inspector metadata supplied by a trusted CopilotKit runtime.
3
+ *
4
+ * Each optional module is independent so clients can render partial metadata
5
+ * from runtimes that do not expose every module.
6
+ */
7
+ export interface InspectorMetadataV1 {
8
+ readonly schemaVersion: 1;
9
+ readonly identity?: {
10
+ readonly organizationName: string;
11
+ readonly projectName: string;
12
+ };
13
+ readonly plan?: {
14
+ readonly code: string;
15
+ readonly label: string;
16
+ };
17
+ readonly license?: {
18
+ readonly state: "valid" | "none" | "expired" | "unknown";
19
+ };
20
+ readonly action?:
21
+ | { readonly kind: "manage_plan"; readonly url: string }
22
+ | { readonly kind: "renew"; readonly url: string }
23
+ | { readonly kind: "enable_intelligence"; readonly url: string };
24
+ readonly usage?: {
25
+ readonly used: number;
26
+ readonly limit:
27
+ | { readonly kind: "finite"; readonly value: number }
28
+ | { readonly kind: "unlimited" }
29
+ | { readonly kind: "unknown" };
30
+ readonly expiringSoonCount?: number;
31
+ };
32
+ }
33
+
34
+ type UnknownRecord = Record<string, unknown>;
35
+ type InspectorIdentity = NonNullable<InspectorMetadataV1["identity"]>;
36
+ type InspectorPlan = NonNullable<InspectorMetadataV1["plan"]>;
37
+ type InspectorLicense = NonNullable<InspectorMetadataV1["license"]>;
38
+ type InspectorAction = NonNullable<InspectorMetadataV1["action"]>;
39
+ type InspectorUsage = NonNullable<InspectorMetadataV1["usage"]>;
40
+ type InspectorUsageLimit = InspectorUsage["limit"];
41
+
42
+ function isRecord(value: unknown): value is UnknownRecord {
43
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
44
+ return false;
45
+ }
46
+
47
+ try {
48
+ const prototype = Object.getPrototypeOf(value);
49
+ return prototype === Object.prototype || prototype === null;
50
+ } catch {
51
+ return false;
52
+ }
53
+ }
54
+
55
+ function readOwnDataProperty(value: UnknownRecord, key: string): unknown {
56
+ try {
57
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
58
+ return descriptor !== undefined && "value" in descriptor
59
+ ? descriptor.value
60
+ : undefined;
61
+ } catch {
62
+ return undefined;
63
+ }
64
+ }
65
+
66
+ function parseNonBlankString(value: unknown): string | undefined {
67
+ if (typeof value !== "string") {
68
+ return undefined;
69
+ }
70
+
71
+ const parsed = value.trim();
72
+ return parsed.length > 0 ? parsed : undefined;
73
+ }
74
+
75
+ function parseIdentity(value: unknown): InspectorIdentity | undefined {
76
+ if (!isRecord(value)) {
77
+ return undefined;
78
+ }
79
+
80
+ const organizationName = parseNonBlankString(value.organizationName);
81
+ const projectName = parseNonBlankString(value.projectName);
82
+ if (organizationName === undefined || projectName === undefined) {
83
+ return undefined;
84
+ }
85
+
86
+ return { organizationName, projectName };
87
+ }
88
+
89
+ function parsePlan(value: unknown): InspectorPlan | undefined {
90
+ if (!isRecord(value)) {
91
+ return undefined;
92
+ }
93
+
94
+ const code = parseNonBlankString(value.code);
95
+ const label = parseNonBlankString(value.label);
96
+ if (code === undefined || label === undefined) {
97
+ return undefined;
98
+ }
99
+
100
+ return { code, label };
101
+ }
102
+
103
+ function parseLicense(value: unknown): InspectorLicense | undefined {
104
+ if (!isRecord(value)) {
105
+ return undefined;
106
+ }
107
+
108
+ switch (value.state) {
109
+ case "valid":
110
+ case "none":
111
+ case "expired":
112
+ case "unknown":
113
+ return { state: value.state };
114
+ default:
115
+ return undefined;
116
+ }
117
+ }
118
+
119
+ function parseActionKind(value: unknown): InspectorAction["kind"] | undefined {
120
+ switch (value) {
121
+ case "manage_plan":
122
+ case "renew":
123
+ case "enable_intelligence":
124
+ return value;
125
+ default:
126
+ return undefined;
127
+ }
128
+ }
129
+
130
+ function parseSafeActionUrl(value: unknown): string | undefined {
131
+ const url = parseNonBlankString(value);
132
+ if (url === undefined || url.includes("?") || url.includes("#")) {
133
+ return undefined;
134
+ }
135
+
136
+ const authorityStart = url.indexOf("://");
137
+ if (authorityStart < 1) {
138
+ return undefined;
139
+ }
140
+
141
+ const authorityAndPath = url.slice(authorityStart + 3);
142
+ const pathStart = authorityAndPath.indexOf("/");
143
+ const authority =
144
+ pathStart === -1 ? authorityAndPath : authorityAndPath.slice(0, pathStart);
145
+ if (authority.includes("@")) {
146
+ return undefined;
147
+ }
148
+
149
+ let parsed: URL;
150
+ try {
151
+ parsed = new URL(url);
152
+ } catch {
153
+ return undefined;
154
+ }
155
+
156
+ if (parsed.hostname.length === 0 || parsed.username || parsed.password) {
157
+ return undefined;
158
+ }
159
+
160
+ if (parsed.protocol === "https:") {
161
+ return url;
162
+ }
163
+
164
+ const isLoopbackHost =
165
+ parsed.hostname === "localhost" ||
166
+ parsed.hostname === "127.0.0.1" ||
167
+ parsed.hostname === "[::1]";
168
+ if (parsed.protocol === "http:" && isLoopbackHost) {
169
+ return url;
170
+ }
171
+
172
+ return undefined;
173
+ }
174
+
175
+ function parseAction(value: unknown): InspectorAction | undefined {
176
+ if (!isRecord(value)) {
177
+ return undefined;
178
+ }
179
+
180
+ const kind = parseActionKind(value.kind);
181
+ const url = parseSafeActionUrl(value.url);
182
+ if (kind === undefined || url === undefined) {
183
+ return undefined;
184
+ }
185
+
186
+ return { kind, url };
187
+ }
188
+
189
+ function isFiniteNonnegativeInteger(value: unknown): value is number {
190
+ return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
191
+ }
192
+
193
+ function parseUsageLimit(value: unknown): InspectorUsageLimit | undefined {
194
+ if (!isRecord(value)) {
195
+ return undefined;
196
+ }
197
+
198
+ if (value.kind === "finite") {
199
+ if (
200
+ typeof value.value !== "number" ||
201
+ !Number.isSafeInteger(value.value) ||
202
+ value.value < 1
203
+ ) {
204
+ return undefined;
205
+ }
206
+
207
+ return { kind: "finite", value: value.value };
208
+ }
209
+
210
+ if (value.kind === "unlimited") {
211
+ return { kind: "unlimited" };
212
+ }
213
+
214
+ if (value.kind === "unknown") {
215
+ return { kind: "unknown" };
216
+ }
217
+
218
+ return undefined;
219
+ }
220
+
221
+ function parseUsage(value: unknown): InspectorUsage | undefined {
222
+ if (!isRecord(value)) {
223
+ return undefined;
224
+ }
225
+
226
+ const limit = parseUsageLimit(value.limit);
227
+ const used = value.used;
228
+ if (!isFiniteNonnegativeInteger(used) || limit === undefined) {
229
+ return undefined;
230
+ }
231
+
232
+ const rawExpiringSoonCount = readOwnDataProperty(value, "expiringSoonCount");
233
+ const expiringSoonCount = isFiniteNonnegativeInteger(rawExpiringSoonCount)
234
+ ? rawExpiringSoonCount
235
+ : undefined;
236
+
237
+ return {
238
+ used,
239
+ limit,
240
+ ...(expiringSoonCount === undefined ? {} : { expiringSoonCount }),
241
+ };
242
+ }
243
+
244
+ /**
245
+ * Parses untrusted inspector metadata without letting one invalid optional
246
+ * module hide the other valid modules.
247
+ *
248
+ * @param value - The decoded runtime response body.
249
+ * @returns Normalized version 1 metadata, or `undefined` for an unsupported
250
+ * top-level payload.
251
+ */
252
+ export function parseInspectorMetadataV1(
253
+ value: unknown,
254
+ ): InspectorMetadataV1 | undefined {
255
+ if (!isRecord(value) || value.schemaVersion !== 1) {
256
+ return undefined;
257
+ }
258
+
259
+ const identity = parseIdentity(value.identity);
260
+ const plan = parsePlan(value.plan);
261
+ const license = parseLicense(value.license);
262
+ const action = parseAction(value.action);
263
+ const usage = parseUsage(value.usage);
264
+
265
+ return {
266
+ schemaVersion: 1,
267
+ ...(identity === undefined ? {} : { identity }),
268
+ ...(plan === undefined ? {} : { plan }),
269
+ ...(license === undefined ? {} : { license }),
270
+ ...(action === undefined ? {} : { action }),
271
+ ...(usage === undefined ? {} : { usage }),
272
+ };
273
+ }
@@ -62,6 +62,8 @@ export interface RuntimeInfo {
62
62
  mode: RuntimeMode;
63
63
  intelligence?: IntelligenceRuntimeInfo;
64
64
  threadEndpoints?: ThreadEndpointRuntimeInfo;
65
+ /** Whether this runtime exposes trusted inspector metadata. */
66
+ inspectorMetadata?: boolean;
65
67
  /**
66
68
  * When true, the runtime exposes POST /agent/:agentId/suggest for stateless
67
69
  * suggestion generation. Absent on older runtimes; clients fall back to a