@copilotkit/sdk-js 1.56.2 → 1.56.4-canary.1777529757

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 (43) hide show
  1. package/dist/langchain.cjs +3 -0
  2. package/dist/langchain.cjs.map +1 -1
  3. package/dist/langchain.d.cts +3 -2
  4. package/dist/langchain.d.mts +3 -2
  5. package/dist/langchain.mjs +3 -2
  6. package/dist/langchain.mjs.map +1 -1
  7. package/dist/langgraph/middleware.cjs +144 -11
  8. package/dist/langgraph/middleware.cjs.map +1 -1
  9. package/dist/langgraph/middleware.d.cts +68 -1
  10. package/dist/langgraph/middleware.d.cts.map +1 -1
  11. package/dist/langgraph/middleware.d.mts +68 -1
  12. package/dist/langgraph/middleware.d.mts.map +1 -1
  13. package/dist/langgraph/middleware.mjs +143 -12
  14. package/dist/langgraph/middleware.mjs.map +1 -1
  15. package/dist/langgraph/state-schema.cjs +37 -0
  16. package/dist/langgraph/state-schema.cjs.map +1 -0
  17. package/dist/langgraph/state-schema.d.cts +118 -0
  18. package/dist/langgraph/state-schema.d.cts.map +1 -0
  19. package/dist/langgraph/state-schema.d.mts +118 -0
  20. package/dist/langgraph/state-schema.d.mts.map +1 -0
  21. package/dist/langgraph/state-schema.mjs +36 -0
  22. package/dist/langgraph/state-schema.mjs.map +1 -0
  23. package/dist/langgraph/types.cjs +41 -0
  24. package/dist/langgraph/types.cjs.map +1 -1
  25. package/dist/langgraph/types.d.cts +140 -51
  26. package/dist/langgraph/types.d.cts.map +1 -1
  27. package/dist/langgraph/types.d.mts +140 -51
  28. package/dist/langgraph/types.d.mts.map +1 -1
  29. package/dist/langgraph/types.mjs +41 -1
  30. package/dist/langgraph/types.mjs.map +1 -1
  31. package/dist/langgraph.cjs +6 -1
  32. package/dist/langgraph.d.cts +4 -3
  33. package/dist/langgraph.d.mts +4 -3
  34. package/dist/langgraph.mjs +4 -3
  35. package/package.json +6 -6
  36. package/src/langchain.ts +4 -0
  37. package/src/langgraph/__tests__/middleware.test.ts +611 -0
  38. package/src/langgraph/__tests__/state-schema.test.ts +85 -0
  39. package/src/langgraph/__tests__/utils.test.ts +254 -0
  40. package/src/langgraph/index.ts +1 -0
  41. package/src/langgraph/middleware.ts +210 -15
  42. package/src/langgraph/state-schema.ts +34 -0
  43. package/src/langgraph/types.ts +57 -0
@@ -0,0 +1,254 @@
1
+ import {
2
+ copilotkitCustomizeConfig,
3
+ convertActionsToDynamicStructuredTools,
4
+ convertActionToDynamicStructuredTool,
5
+ } from "../utils";
6
+
7
+ describe("copilotkitCustomizeConfig", () => {
8
+ it("returns config unchanged when no options provided", () => {
9
+ const baseConfig = { metadata: { existing: true } };
10
+ const result = copilotkitCustomizeConfig(baseConfig);
11
+ expect(result.metadata).toEqual({ existing: true });
12
+ });
13
+
14
+ it("returns config unchanged when options is empty object", () => {
15
+ const baseConfig = { metadata: {} };
16
+ const result = copilotkitCustomizeConfig(baseConfig, {});
17
+ expect(result.metadata).toEqual({});
18
+ });
19
+
20
+ it("sets emit-messages metadata flag to false", () => {
21
+ const result = copilotkitCustomizeConfig({}, { emitMessages: false });
22
+ expect(result.metadata!["copilotkit:emit-messages"]).toBe(false);
23
+ });
24
+
25
+ it("sets emit-messages metadata flag to true", () => {
26
+ const result = copilotkitCustomizeConfig({}, { emitMessages: true });
27
+ expect(result.metadata!["copilotkit:emit-messages"]).toBe(true);
28
+ });
29
+
30
+ it("sets emit-tool-calls metadata flag to false", () => {
31
+ const result = copilotkitCustomizeConfig({}, { emitToolCalls: false });
32
+ expect(result.metadata!["copilotkit:emit-tool-calls"]).toBe(false);
33
+ });
34
+
35
+ it("sets emit-tool-calls to a specific tool name string", () => {
36
+ const result = copilotkitCustomizeConfig(
37
+ {},
38
+ { emitToolCalls: "SearchTool" },
39
+ );
40
+ expect(result.metadata!["copilotkit:emit-tool-calls"]).toBe("SearchTool");
41
+ });
42
+
43
+ it("sets emit-tool-calls to an array of tool names", () => {
44
+ const result = copilotkitCustomizeConfig(
45
+ {},
46
+ { emitToolCalls: ["SearchTool", "FetchTool"] },
47
+ );
48
+ expect(result.metadata!["copilotkit:emit-tool-calls"]).toEqual([
49
+ "SearchTool",
50
+ "FetchTool",
51
+ ]);
52
+ });
53
+
54
+ it("sets both emit flags together", () => {
55
+ const result = copilotkitCustomizeConfig(
56
+ {},
57
+ {
58
+ emitMessages: false,
59
+ emitToolCalls: false,
60
+ },
61
+ );
62
+ expect(result.metadata!["copilotkit:emit-messages"]).toBe(false);
63
+ expect(result.metadata!["copilotkit:emit-tool-calls"]).toBe(false);
64
+ });
65
+
66
+ it("emitAll sets both messages and tool-calls to true", () => {
67
+ const result = copilotkitCustomizeConfig({}, { emitAll: true });
68
+ expect(result.metadata!["copilotkit:emit-messages"]).toBe(true);
69
+ expect(result.metadata!["copilotkit:emit-tool-calls"]).toBe(true);
70
+ });
71
+
72
+ it("converts emitIntermediateState to snake_case in metadata", () => {
73
+ const result = copilotkitCustomizeConfig(
74
+ {},
75
+ {
76
+ emitIntermediateState: [
77
+ { stateKey: "steps", tool: "SearchTool", toolArgument: "steps" },
78
+ ],
79
+ },
80
+ );
81
+ const intermediateState =
82
+ result.metadata!["copilotkit:emit-intermediate-state"];
83
+ expect(intermediateState).toHaveLength(1);
84
+ expect(intermediateState[0]).toEqual({
85
+ state_key: "steps",
86
+ tool: "SearchTool",
87
+ tool_argument: "steps",
88
+ });
89
+ });
90
+
91
+ it("handles emitIntermediateState without toolArgument", () => {
92
+ const result = copilotkitCustomizeConfig(
93
+ {},
94
+ {
95
+ emitIntermediateState: [{ stateKey: "output", tool: "WriteTool" }],
96
+ },
97
+ );
98
+ const intermediateState =
99
+ result.metadata!["copilotkit:emit-intermediate-state"];
100
+ expect(intermediateState[0]).toEqual({
101
+ state_key: "output",
102
+ tool: "WriteTool",
103
+ tool_argument: undefined,
104
+ });
105
+ });
106
+
107
+ it("throws when emitIntermediateState item is missing stateKey", () => {
108
+ expect(() => {
109
+ copilotkitCustomizeConfig(
110
+ {},
111
+ {
112
+ emitIntermediateState: [{ tool: "SearchTool" } as any],
113
+ },
114
+ );
115
+ }).toThrow("stateKey");
116
+ });
117
+
118
+ it("throws when emitIntermediateState item is missing tool", () => {
119
+ expect(() => {
120
+ copilotkitCustomizeConfig(
121
+ {},
122
+ {
123
+ emitIntermediateState: [{ stateKey: "steps" } as any],
124
+ },
125
+ );
126
+ }).toThrow("tool");
127
+ });
128
+
129
+ it("preserves existing metadata from baseConfig", () => {
130
+ const result = copilotkitCustomizeConfig(
131
+ { metadata: { "custom-key": "custom-value" } },
132
+ { emitMessages: false },
133
+ );
134
+ expect(result.metadata!["custom-key"]).toBe("custom-value");
135
+ expect(result.metadata!["copilotkit:emit-messages"]).toBe(false);
136
+ });
137
+
138
+ it("handles null/undefined baseConfig gracefully", () => {
139
+ const result = copilotkitCustomizeConfig(null as any, {
140
+ emitMessages: false,
141
+ });
142
+ expect(result.metadata!["copilotkit:emit-messages"]).toBe(false);
143
+ });
144
+ });
145
+
146
+ describe("convertActionToDynamicStructuredTool", () => {
147
+ it("converts a valid action to DynamicStructuredTool", () => {
148
+ const action = {
149
+ name: "myTool",
150
+ description: "A test tool",
151
+ parameters: {
152
+ type: "object",
153
+ properties: {
154
+ query: { type: "string" },
155
+ },
156
+ },
157
+ };
158
+ const tool = convertActionToDynamicStructuredTool(action);
159
+ expect(tool.name).toBe("myTool");
160
+ expect(tool.description).toBe("A test tool");
161
+ });
162
+
163
+ it("throws when actionInput is null", () => {
164
+ expect(() => convertActionToDynamicStructuredTool(null)).toThrow(
165
+ "Action input is required",
166
+ );
167
+ });
168
+
169
+ it("throws when name is missing", () => {
170
+ expect(() =>
171
+ convertActionToDynamicStructuredTool({
172
+ description: "test",
173
+ parameters: {},
174
+ }),
175
+ ).toThrow("name");
176
+ });
177
+
178
+ it("throws when description is missing", () => {
179
+ expect(() =>
180
+ convertActionToDynamicStructuredTool({ name: "test", parameters: {} }),
181
+ ).toThrow("description");
182
+ });
183
+
184
+ it("throws when parameters is missing", () => {
185
+ expect(() =>
186
+ convertActionToDynamicStructuredTool({
187
+ name: "test",
188
+ description: "test",
189
+ }),
190
+ ).toThrow("parameters");
191
+ });
192
+ });
193
+
194
+ describe("convertActionsToDynamicStructuredTools", () => {
195
+ it("converts multiple actions", () => {
196
+ const actions = [
197
+ {
198
+ name: "tool1",
199
+ description: "First tool",
200
+ parameters: { type: "object", properties: {} },
201
+ },
202
+ {
203
+ name: "tool2",
204
+ description: "Second tool",
205
+ parameters: { type: "object", properties: {} },
206
+ },
207
+ ];
208
+ const tools = convertActionsToDynamicStructuredTools(actions);
209
+ expect(tools).toHaveLength(2);
210
+ expect(tools[0].name).toBe("tool1");
211
+ expect(tools[1].name).toBe("tool2");
212
+ });
213
+
214
+ it("returns empty array for empty input", () => {
215
+ const tools = convertActionsToDynamicStructuredTools([]);
216
+ expect(tools).toEqual([]);
217
+ });
218
+
219
+ it("handles { type: 'function', function: {...} } format", () => {
220
+ const actions = [
221
+ {
222
+ type: "function",
223
+ function: {
224
+ name: "wrappedTool",
225
+ description: "A wrapped tool",
226
+ parameters: { type: "object", properties: {} },
227
+ },
228
+ },
229
+ ];
230
+ const tools = convertActionsToDynamicStructuredTools(actions);
231
+ expect(tools).toHaveLength(1);
232
+ expect(tools[0].name).toBe("wrappedTool");
233
+ });
234
+
235
+ it("throws when input is not an array", () => {
236
+ expect(() =>
237
+ convertActionsToDynamicStructuredTools("not-array" as any),
238
+ ).toThrow("Actions must be an array");
239
+ });
240
+
241
+ it("wraps individual action errors with index info", () => {
242
+ const actions = [
243
+ {
244
+ name: "goodTool",
245
+ description: "works",
246
+ parameters: { type: "object", properties: {} },
247
+ },
248
+ { name: "badTool" }, // missing description and parameters
249
+ ];
250
+ expect(() => convertActionsToDynamicStructuredTools(actions)).toThrow(
251
+ "index 1",
252
+ );
253
+ });
254
+ });
@@ -1,3 +1,4 @@
1
1
  export * from "./types";
2
2
  export * from "./utils";
3
3
  export * from "./middleware";
4
+ export * from "./state-schema";
@@ -1,7 +1,175 @@
1
1
  import { createMiddleware, AIMessage, SystemMessage } from "langchain";
2
2
  import type { InteropZodObject } from "@langchain/core/utils/types";
3
+ import type {
4
+ StandardJSONSchemaV1,
5
+ StandardSchemaV1,
6
+ } from "@standard-schema/spec";
3
7
  import * as z from "zod";
4
8
 
9
+ type WithJsonSchema<T> = T extends { "~standard": infer S }
10
+ ? Omit<T, "~standard"> & {
11
+ "~standard": S &
12
+ StandardJSONSchemaV1.Props<
13
+ S extends StandardSchemaV1.Props<infer I, any> ? I : unknown,
14
+ S extends StandardSchemaV1.Props<any, infer O> ? O : unknown
15
+ >;
16
+ }
17
+ : T;
18
+
19
+ /**
20
+ * Augment a Standard-Schema–compatible schema (e.g. Zod) with a
21
+ * `~standard.jsonSchema.input` hook so LangGraph's
22
+ * `getJsonSchemaFromSchema` (called from `StateSchema.getJsonSchema`)
23
+ * can serialize the field.
24
+ *
25
+ * Without this, Zod v4 fields carry `~standard.validate` + `vendor` only,
26
+ * and `isStandardJSONSchema()` returns false, so the field is silently
27
+ * dropped from the graph's `output_schema`. That makes AG-UI
28
+ * `STATE_SNAPSHOT` events filter the field out of the payload sent to
29
+ * the frontend even though the underlying thread state has the value.
30
+ *
31
+ * Use this on any custom state field you want visible to the frontend
32
+ * via `useAgent().state.*`.
33
+ *
34
+ * @example
35
+ * ```ts
36
+ * import { zodState } from "@copilotkit/sdk-js/langgraph";
37
+ *
38
+ * const stateSchema = z.object({
39
+ * todos: zodState(z.array(TodoSchema).default(() => [])),
40
+ * });
41
+ * ```
42
+ */
43
+ export function zodState<T extends object>(schema: T): WithJsonSchema<T> {
44
+ const std = (schema as { "~standard"?: { jsonSchema?: unknown } })[
45
+ "~standard"
46
+ ];
47
+ if (std && typeof std === "object" && !("jsonSchema" in std)) {
48
+ let cached: Record<string, unknown> | undefined;
49
+ std.jsonSchema = {
50
+ input: () => {
51
+ if (cached) return cached;
52
+ // Prefer zod-v4's native `toJSONSchema` when available. Falls back to
53
+ // an empty object, which is sufficient for the field to appear in the
54
+ // graph's output_schema (langgraph-api treats it as an opaque field).
55
+ try {
56
+ const maybeV4ToJsonSchema = (
57
+ z as unknown as {
58
+ toJSONSchema?: (s: unknown) => Record<string, unknown>;
59
+ }
60
+ ).toJSONSchema;
61
+ cached =
62
+ typeof maybeV4ToJsonSchema === "function"
63
+ ? maybeV4ToJsonSchema(schema)
64
+ : {};
65
+ } catch {
66
+ cached = {};
67
+ }
68
+ return cached;
69
+ },
70
+ };
71
+ }
72
+ return schema as WithJsonSchema<T>;
73
+ }
74
+
75
+ /**
76
+ * Internal/framework state keys that should never be auto-surfaced to the
77
+ * LLM as user-facing state. These are reducer-managed message buckets,
78
+ * CopilotKit/AG-UI plumbing, or graph-internal scaffolding.
79
+ */
80
+ const RESERVED_STATE_KEYS: ReadonlySet<string> = new Set([
81
+ "messages",
82
+ "copilotkit",
83
+ "ag-ui",
84
+ "tools",
85
+ "structured_response",
86
+ "thread_id",
87
+ "remaining_steps",
88
+ ]);
89
+
90
+ /**
91
+ * Controls how user-defined state keys are surfaced into the LLM prompt
92
+ * on every model call. Off by default to avoid leaking arbitrary state
93
+ * into prompts; opt in explicitly.
94
+ *
95
+ * - `false` (default) — never surface state.
96
+ * - `true` — every state key not in the reserved internal set and not
97
+ * prefixed with `_` is JSON-serialized into a "Current agent state:"
98
+ * note appended to the system prompt.
99
+ * - `string[]` — only surface the named keys (use this when you want
100
+ * explicit control over what the LLM sees, e.g. `["liked", "todos"]`).
101
+ */
102
+ export type ExposeStateOption = boolean | readonly string[];
103
+
104
+ const buildStateNote = (
105
+ state: Record<string, unknown>,
106
+ expose: ExposeStateOption,
107
+ ): string | null => {
108
+ if (expose === false) return null;
109
+
110
+ const allow: ReadonlySet<string> | null = Array.isArray(expose)
111
+ ? new Set(expose)
112
+ : null;
113
+
114
+ const snapshot: Record<string, unknown> = {};
115
+ for (const key of Object.keys(state)) {
116
+ if (
117
+ allow
118
+ ? !allow.has(key)
119
+ : RESERVED_STATE_KEYS.has(key) || key.startsWith("_")
120
+ ) {
121
+ continue;
122
+ }
123
+ const value = state[key];
124
+ if (
125
+ value === undefined ||
126
+ value === null ||
127
+ value === "" ||
128
+ (Array.isArray(value) && value.length === 0) ||
129
+ (typeof value === "object" &&
130
+ !Array.isArray(value) &&
131
+ Object.keys(value as Record<string, unknown>).length === 0)
132
+ ) {
133
+ continue;
134
+ }
135
+ snapshot[key] = value;
136
+ }
137
+
138
+ if (Object.keys(snapshot).length === 0) return null;
139
+
140
+ let body: string;
141
+ try {
142
+ body = JSON.stringify(snapshot, null, 2);
143
+ } catch {
144
+ body = String(snapshot);
145
+ }
146
+ return `Current agent state:\n${body}`;
147
+ };
148
+
149
+ const applyStateNote = (request: any, expose: ExposeStateOption): any => {
150
+ const note = buildStateNote(
151
+ (request.state ?? {}) as Record<string, unknown>,
152
+ expose,
153
+ );
154
+ if (!note) return request;
155
+
156
+ const existing = request.systemPrompt;
157
+ if (existing == null) {
158
+ return { ...request, systemPrompt: new SystemMessage({ content: note }) };
159
+ }
160
+ // existing may be a string OR a SystemMessage
161
+ const baseText =
162
+ typeof existing === "string"
163
+ ? existing
164
+ : typeof existing.content === "string"
165
+ ? existing.content
166
+ : String(existing.content);
167
+ return {
168
+ ...request,
169
+ systemPrompt: new SystemMessage({ content: `${baseText}\n\n${note}` }),
170
+ };
171
+ };
172
+
5
173
  const createAppContextBeforeAgent = (state, runtime) => {
6
174
  const messages = state.messages;
7
175
 
@@ -117,23 +285,26 @@ const createAppContextBeforeAgent = (state, runtime) => {
117
285
  * ```
118
286
  */
119
287
  const copilotKitStateSchema = z.object({
120
- copilotkit: z
121
- .object({
122
- actions: z.array(z.any()),
123
- context: z.any().optional(),
124
- interceptedToolCalls: z.array(z.any()).optional(),
125
- originalAIMessageId: z.string().optional(),
126
- })
127
- .optional(),
288
+ copilotkit: zodState(
289
+ z
290
+ .object({
291
+ actions: z.array(z.any()),
292
+ context: z.any().optional(),
293
+ interceptedToolCalls: z.array(z.any()).optional(),
294
+ originalAIMessageId: z.string().optional(),
295
+ })
296
+ .optional(),
297
+ ),
128
298
  });
129
299
 
130
- const middlewareInput = {
300
+ const buildMiddlewareInput = (exposeState: ExposeStateOption) => ({
131
301
  name: "CopilotKitMiddleware",
132
302
 
133
303
  stateSchema: copilotKitStateSchema as unknown as InteropZodObject,
134
304
 
135
- // Inject frontend tools before model call
136
- wrapModelCall: async (request, handler) => {
305
+ // Inject frontend tools and surface user state before model call
306
+ wrapModelCall: async (request: any, handler: (req: any) => Promise<any>) => {
307
+ request = applyStateNote(request, exposeState);
137
308
  const frontendTools = request.state["copilotkit"]?.actions ?? [];
138
309
 
139
310
  if (frontendTools.length === 0) {
@@ -234,9 +405,33 @@ const middlewareInput = {
234
405
  },
235
406
  };
236
407
  },
237
- } as any;
238
- const createCopilotKitMiddleware = () => {
239
- return createMiddleware(middlewareInput);
408
+ });
409
+
410
+ /**
411
+ * Build a CopilotKit middleware instance with custom options.
412
+ *
413
+ * Use this when you want to override the default state-exposure behavior
414
+ * (for example to hide a sensitive key, or to use an explicit allowlist).
415
+ *
416
+ * @example
417
+ * ```typescript
418
+ * import { createCopilotkitMiddleware } from "@copilotkit/sdk-js/langgraph";
419
+ *
420
+ * const middleware = createCopilotkitMiddleware({
421
+ * exposeState: ["liked", "todos"],
422
+ * });
423
+ * ```
424
+ */
425
+ export const createCopilotkitMiddleware = (
426
+ options: { exposeState?: ExposeStateOption } = {},
427
+ ) => {
428
+ const exposeState = options.exposeState ?? false;
429
+ return createMiddleware(buildMiddlewareInput(exposeState) as any);
240
430
  };
241
431
 
242
- export const copilotkitMiddleware = createCopilotKitMiddleware();
432
+ /**
433
+ * Default CopilotKit middleware singleton — does NOT surface user state
434
+ * to the LLM. Pass `exposeState: true` (or an allowlist) to
435
+ * {@link createCopilotkitMiddleware} to opt in.
436
+ */
437
+ export const copilotkitMiddleware = createCopilotkitMiddleware();
@@ -0,0 +1,34 @@
1
+ import { MessagesValue, StateSchema } from "@langchain/langgraph";
2
+ import { CopilotKitPropertiesSchema } from "./types";
3
+
4
+ /**
5
+ * CopilotKit agent state defined with LangGraph's modern
6
+ * [`StateSchema`](https://docs.langchain.com/oss/javascript/langgraph/graph-api)
7
+ * API.
8
+ *
9
+ * Prefer this over `CopilotKitStateAnnotation` when starting a new
10
+ * TypeScript agent. `Annotation.Root` is still supported by LangGraph but
11
+ * `StateSchema` is the recommended API going forward.
12
+ *
13
+ * ### Example
14
+ *
15
+ * ```typescript
16
+ * import { StateSchema } from "@langchain/langgraph";
17
+ * import { CopilotKitStateSchema } from "@copilotkit/sdk-js/langgraph";
18
+ * import { z } from "zod";
19
+ *
20
+ * export const AgentStateSchema = new StateSchema({
21
+ * language: z.enum(["english", "spanish"]),
22
+ * ...CopilotKitStateSchema.fields,
23
+ * });
24
+ *
25
+ * export type AgentState = typeof AgentStateSchema.State;
26
+ * ```
27
+ */
28
+ export const CopilotKitStateSchema = new StateSchema({
29
+ copilotkit: CopilotKitPropertiesSchema,
30
+ messages: MessagesValue,
31
+ });
32
+
33
+ export type CopilotKitSchemaState = typeof CopilotKitStateSchema.State;
34
+ export type CopilotKitSchemaUpdate = typeof CopilotKitStateSchema.Update;
@@ -1,5 +1,20 @@
1
1
  import { Annotation, MessagesAnnotation } from "@langchain/langgraph";
2
2
 
3
+ export interface StandardSerializableSchema<Input, Output = Input> {
4
+ readonly "~standard": {
5
+ readonly version: 1;
6
+ readonly vendor: string;
7
+ readonly validate: (
8
+ value: unknown,
9
+ ) => { value: Output } | { issues: ReadonlyArray<{ message: string }> };
10
+ readonly types?: { readonly input: Input; readonly output: Output };
11
+ readonly jsonSchema: {
12
+ readonly input: (options: { target: string }) => Record<string, unknown>;
13
+ readonly output: (options: { target: string }) => Record<string, unknown>;
14
+ };
15
+ };
16
+ }
17
+
3
18
  export const CopilotKitPropertiesAnnotation = Annotation.Root({
4
19
  actions: Annotation<any[]>,
5
20
  context: Annotation<{ description: string; value: string }[]>,
@@ -12,6 +27,48 @@ export const CopilotKitStateAnnotation = Annotation.Root({
12
27
  ...MessagesAnnotation.spec,
13
28
  });
14
29
 
30
+ const COPILOTKIT_PROPERTIES_JSON_SCHEMA = {
31
+ type: "object",
32
+ properties: {
33
+ actions: { type: "array", items: {} },
34
+ context: {
35
+ type: "array",
36
+ items: {
37
+ type: "object",
38
+ properties: {
39
+ description: { type: "string" },
40
+ value: { type: "string" },
41
+ },
42
+ required: ["description", "value"],
43
+ },
44
+ },
45
+ interceptedToolCalls: { type: "array", items: {} },
46
+ originalAIMessageId: { type: "string" },
47
+ },
48
+ };
49
+
50
+ /**
51
+ * Standard Schema describing the `copilotkit` field on agent state.
52
+ *
53
+ * CopilotKit populates these fields at runtime, so the schema accepts any
54
+ * input shape. Use it with `new StateSchema({ ...CopilotKitStateSchema.fields })`.
55
+ */
56
+ export const CopilotKitPropertiesSchema: StandardSerializableSchema<
57
+ typeof CopilotKitPropertiesAnnotation.State
58
+ > = {
59
+ "~standard": {
60
+ version: 1,
61
+ vendor: "@copilotkit/sdk-js",
62
+ validate: (value) => ({
63
+ value: value as typeof CopilotKitPropertiesAnnotation.State,
64
+ }),
65
+ jsonSchema: {
66
+ input: () => COPILOTKIT_PROPERTIES_JSON_SCHEMA,
67
+ output: () => COPILOTKIT_PROPERTIES_JSON_SCHEMA,
68
+ },
69
+ },
70
+ };
71
+
15
72
  export interface IntermediateStateConfig {
16
73
  stateKey: string;
17
74
  tool: string;