@copilotkit/shared 1.55.3-canary.1776260990 → 1.56.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.
@@ -0,0 +1,116 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { resolveDebugConfig } from "../debug";
3
+
4
+ describe("resolveDebugConfig", () => {
5
+ it("returns all-off for undefined", () => {
6
+ expect(resolveDebugConfig(undefined)).toEqual({
7
+ enabled: false,
8
+ events: false,
9
+ lifecycle: false,
10
+ verbose: false,
11
+ });
12
+ });
13
+
14
+ it("returns all-off for false", () => {
15
+ expect(resolveDebugConfig(false)).toEqual({
16
+ enabled: false,
17
+ events: false,
18
+ lifecycle: false,
19
+ verbose: false,
20
+ });
21
+ });
22
+
23
+ it("returns events + lifecycle on but verbose off for true (PII safety)", () => {
24
+ expect(resolveDebugConfig(true)).toEqual({
25
+ enabled: true,
26
+ events: true,
27
+ lifecycle: true,
28
+ verbose: false,
29
+ });
30
+ });
31
+
32
+ it("allows explicit verbose opt-in via object shorthand", () => {
33
+ expect(resolveDebugConfig({ verbose: true })).toEqual({
34
+ enabled: true,
35
+ events: true,
36
+ lifecycle: true,
37
+ verbose: true,
38
+ });
39
+ });
40
+
41
+ it("allows explicit verbose opt-in with events", () => {
42
+ expect(resolveDebugConfig({ events: true, verbose: true })).toEqual({
43
+ enabled: true,
44
+ events: true,
45
+ lifecycle: true,
46
+ verbose: true,
47
+ });
48
+ });
49
+
50
+ it("defaults events and lifecycle to true, verbose to false for empty object", () => {
51
+ expect(resolveDebugConfig({})).toEqual({
52
+ enabled: true,
53
+ events: true,
54
+ lifecycle: true,
55
+ verbose: false,
56
+ });
57
+ });
58
+
59
+ it("respects explicit events: true", () => {
60
+ expect(resolveDebugConfig({ events: true })).toEqual({
61
+ enabled: true,
62
+ events: true,
63
+ lifecycle: true,
64
+ verbose: false,
65
+ });
66
+ });
67
+
68
+ it("respects explicit events: false (lifecycle still defaults true)", () => {
69
+ expect(resolveDebugConfig({ events: false })).toEqual({
70
+ enabled: true,
71
+ events: false,
72
+ lifecycle: true,
73
+ verbose: false,
74
+ });
75
+ });
76
+
77
+ it("respects explicit lifecycle: false (events still defaults true)", () => {
78
+ expect(resolveDebugConfig({ lifecycle: false })).toEqual({
79
+ enabled: true,
80
+ events: true,
81
+ lifecycle: false,
82
+ verbose: false,
83
+ });
84
+ });
85
+
86
+ it("sets enabled to false when both events and lifecycle are false", () => {
87
+ expect(resolveDebugConfig({ events: false, lifecycle: false })).toEqual({
88
+ enabled: false,
89
+ events: false,
90
+ lifecycle: false,
91
+ verbose: false,
92
+ });
93
+ });
94
+
95
+ it("clamps verbose to false when enabled is false (events and lifecycle both false)", () => {
96
+ expect(
97
+ resolveDebugConfig({ events: false, lifecycle: false, verbose: true }),
98
+ ).toEqual({
99
+ enabled: false,
100
+ events: false,
101
+ lifecycle: false,
102
+ verbose: false,
103
+ });
104
+ });
105
+
106
+ it("handles mixed config: events true, lifecycle false, verbose true", () => {
107
+ expect(
108
+ resolveDebugConfig({ events: true, lifecycle: false, verbose: true }),
109
+ ).toEqual({
110
+ enabled: true,
111
+ events: true,
112
+ lifecycle: false,
113
+ verbose: true,
114
+ });
115
+ });
116
+ });
@@ -47,29 +47,38 @@ CRITICAL: Do NOT use "/name" (absolute) inside templates — use "name" (relativ
47
47
  The container's path ("/items") uses a leading slash (absolute), but all
48
48
  components INSIDE the template use paths WITHOUT leading slash.
49
49
 
50
- DATA MODEL:
51
- The "data" key in the tool args is a plain JSON object that initializes the surface
52
- data model. Components bound to paths (e.g. "value": { "path": "/form/name" })
53
- read from and write to this data model. Examples:
54
- For forms: "data": { "form": { "name": "Alice", "email": "" } }
55
- For lists: "data": { "items": [{"name": "Product A"}, {"name": "Product B"}] }
56
- For mixed: "data": { "form": { "query": "" }, "results": [...] }
50
+ COMPONENT VALUES — DEFAULT RULE:
51
+ Use inline literal values for ALL component properties. Pass strings, numbers,
52
+ arrays, and objects directly on the component. Do NOT use { "path": "..." }
53
+ objects unless the property's schema explicitly allows it (see exception below).
54
+ CRITICAL: USING { "path": "..." } ON A PROPERTY THAT DOES NOT DECLARE PATH
55
+ SUPPORT IN ITS SCHEMA WILL CAUSE A RUNTIME CRASH AND BREAK THE ENTIRE UI.
56
+ ALWAYS CHECK THE COMPONENT SCHEMA FIRST IF THE PROPERTY ONLY ACCEPTS A
57
+ PLAIN TYPE, YOU MUST USE A LITERAL VALUE.
58
+ VERY IMPORTANT: THE APPLICATION WILL BREAK IF YOU DO NOT FOLLOW THIS RULE!
57
59
 
58
- FORMS AND TWO-WAY DATA BINDING:
59
- To create editable forms, bind input components to data model paths using { "path": "..." }.
60
- The client automatically writes user input back to the data model at the bound path.
61
- CRITICAL: Using a literal value (e.g. "value": "") makes the field READ-ONLY.
62
- You MUST use { "path": "..." } to make inputs editable.
60
+ For example, a chart's "data" must always be an inline array:
61
+ "data": [{"label": "Jan", "value": 100}, {"label": "Feb", "value": 200}]
62
+ A metric's "value" must always be an inline string:
63
+ "value": "$1,200"
63
64
 
64
- Input components use "value" as the binding property:
65
- "value": { "path": "/form/fieldName" }
65
+ PATH BINDING EXCEPTION SCHEMA-DRIVEN:
66
+ A few properties accept { "path": "/some/path" } as an alternative to a literal
67
+ value. You can identify these in the Available Components schema: the property
68
+ will list BOTH a literal type AND an object-with-path option. If a property only
69
+ shows a single type (string, number, array, etc.), it does NOT support path
70
+ binding — use a literal value only.
66
71
 
67
- To retrieve form values when a button is clicked, include "context" with path references
68
- in the button's action. Paths are resolved to their current values at click time:
69
- "action": { "event": { "name": "submit", "context": { "userName": { "path": "/form/name" } } } }
72
+ Path binding is typically used for editable form inputs so the client can write
73
+ user input back to the data model. When building forms:
74
+ - Bind input "value" to a data model path: "value": { "path": "/form/name" }
75
+ - Pre-fill via the "data" tool argument: "data": { "form": { "name": "Alice" } }
76
+ - Capture values on submit via button action context:
77
+ "action": { "event": { "name": "submit", "context": { "name": { "path": "/form/name" } } } }
70
78
 
71
- To pre-fill form values, pass initial data via the "data" tool argument:
72
- "data": { "form": { "name": "Markus" } }`;
79
+ REPEATING CONTENT uses a structural children format (not the same as value binding):
80
+ children: { componentId: "card-id", path: "/items" }
81
+ Components inside templates use RELATIVE paths (no leading slash): { "path": "name" }.`;
73
82
 
74
83
  /**
75
84
  * Design guidelines — visual design rules, component hierarchy tips,
@@ -94,8 +103,8 @@ Design principles:
94
103
  "action": { "event": { "name": "myAction", "context": { "key": "value" } } }
95
104
  The "event" key holds an OBJECT with "name" (required) and "context" (optional).
96
105
  Do NOT use a flat format like {"event": "name"} — "event" must be an object.
97
- - For forms: every input MUST use path binding on the "value" property
98
- (e.g. "value": { "path": "/form/name" }) to be editable. The submit button's
99
- action context MUST reference the same paths to capture the user's input.
106
+ - For forms: check the component schema if an input's "value" property
107
+ supports path binding, use it for editable fields. The submit button's
108
+ action context should reference the same paths to capture user input.
100
109
 
101
110
  Use the SAME surfaceId as the main surface. Match action names to button action event names.`;
package/src/debug.ts ADDED
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Granular debug configuration for CopilotKit runtime and client.
3
+ * Pass `true` to enable events + lifecycle logging (but NOT verbose payloads),
4
+ * or an object for granular control including `verbose: true` for full payloads.
5
+ */
6
+ export type DebugConfig =
7
+ | boolean
8
+ | {
9
+ /** Log every event emitted/received. Default: true */
10
+ events?: boolean;
11
+ /** Log request/run lifecycle. Default: true */
12
+ lifecycle?: boolean;
13
+ /** Log full event payloads instead of summaries. Default: false — must be explicitly opted in */
14
+ verbose?: boolean;
15
+ };
16
+
17
+ /** Normalized debug configuration — all fields resolved to booleans. */
18
+ export interface ResolvedDebugConfig {
19
+ enabled: boolean;
20
+ events: boolean;
21
+ lifecycle: boolean;
22
+ verbose: boolean;
23
+ }
24
+
25
+ /** The all-off config used when debug is falsy. */
26
+ const DEBUG_OFF: ResolvedDebugConfig = {
27
+ enabled: false,
28
+ events: false,
29
+ lifecycle: false,
30
+ verbose: false,
31
+ };
32
+
33
+ /**
34
+ * Normalizes a DebugConfig value into a ResolvedDebugConfig.
35
+ *
36
+ * - `false` / `undefined` → all off
37
+ * - `true` → events + lifecycle on, verbose off (no PII in logs)
38
+ * - object → merges with defaults (events: true, lifecycle: true, verbose: false)
39
+ */
40
+ export function resolveDebugConfig(
41
+ debug: DebugConfig | undefined,
42
+ ): ResolvedDebugConfig {
43
+ if (!debug) return DEBUG_OFF;
44
+
45
+ if (debug === true) {
46
+ return { enabled: true, events: true, lifecycle: true, verbose: false };
47
+ }
48
+
49
+ const events = debug.events ?? true;
50
+ const lifecycle = debug.lifecycle ?? true;
51
+ const enabled = events || lifecycle;
52
+ const verbose = enabled && (debug.verbose ?? false);
53
+
54
+ return { enabled, events, lifecycle, verbose };
55
+ }
package/src/index.ts CHANGED
@@ -2,6 +2,7 @@ export * from "./types";
2
2
  export * from "./utils";
3
3
  export * from "./constants";
4
4
  export * from "./telemetry";
5
+ export * from "./debug";
5
6
  export * from "./standard-schema";
6
7
  export * from "./attachments";
7
8