@dex-ai/sdk 0.1.30

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 (85) hide show
  1. package/README.md +308 -0
  2. package/dist/agent.d.ts +181 -0
  3. package/dist/agent.d.ts.map +1 -0
  4. package/dist/agent.js +41 -0
  5. package/dist/agent.js.map +1 -0
  6. package/dist/context.d.ts +68 -0
  7. package/dist/context.d.ts.map +1 -0
  8. package/dist/context.js +8 -0
  9. package/dist/context.js.map +1 -0
  10. package/dist/create-agent.d.ts +7 -0
  11. package/dist/create-agent.d.ts.map +1 -0
  12. package/dist/create-agent.js +205 -0
  13. package/dist/create-agent.js.map +1 -0
  14. package/dist/extension.d.ts +162 -0
  15. package/dist/extension.d.ts.map +1 -0
  16. package/dist/extension.js +20 -0
  17. package/dist/extension.js.map +1 -0
  18. package/dist/generate.d.ts +10 -0
  19. package/dist/generate.d.ts.map +1 -0
  20. package/dist/generate.js +839 -0
  21. package/dist/generate.js.map +1 -0
  22. package/dist/index.d.ts +26 -0
  23. package/dist/index.d.ts.map +1 -0
  24. package/dist/index.js +16 -0
  25. package/dist/index.js.map +1 -0
  26. package/dist/message.d.ts +89 -0
  27. package/dist/message.d.ts.map +1 -0
  28. package/dist/message.js +17 -0
  29. package/dist/message.js.map +1 -0
  30. package/dist/messages.d.ts +98 -0
  31. package/dist/messages.d.ts.map +1 -0
  32. package/dist/messages.js +339 -0
  33. package/dist/messages.js.map +1 -0
  34. package/dist/model.d.ts +39 -0
  35. package/dist/model.d.ts.map +1 -0
  36. package/dist/model.js +11 -0
  37. package/dist/model.js.map +1 -0
  38. package/dist/provider.d.ts +157 -0
  39. package/dist/provider.d.ts.map +1 -0
  40. package/dist/provider.js +39 -0
  41. package/dist/provider.js.map +1 -0
  42. package/dist/resolve-schema.d.ts +44 -0
  43. package/dist/resolve-schema.d.ts.map +1 -0
  44. package/dist/resolve-schema.js +367 -0
  45. package/dist/resolve-schema.js.map +1 -0
  46. package/dist/schema.d.ts +80 -0
  47. package/dist/schema.d.ts.map +1 -0
  48. package/dist/schema.js +90 -0
  49. package/dist/schema.js.map +1 -0
  50. package/dist/tool-dispatch.d.ts +24 -0
  51. package/dist/tool-dispatch.d.ts.map +1 -0
  52. package/dist/tool-dispatch.js +120 -0
  53. package/dist/tool-dispatch.js.map +1 -0
  54. package/dist/tool-result-cache.d.ts +43 -0
  55. package/dist/tool-result-cache.d.ts.map +1 -0
  56. package/dist/tool-result-cache.js +118 -0
  57. package/dist/tool-result-cache.js.map +1 -0
  58. package/dist/tool.d.ts +96 -0
  59. package/dist/tool.d.ts.map +1 -0
  60. package/dist/tool.js +29 -0
  61. package/dist/tool.js.map +1 -0
  62. package/dist/util.d.ts +26 -0
  63. package/dist/util.d.ts.map +1 -0
  64. package/dist/util.js +104 -0
  65. package/dist/util.js.map +1 -0
  66. package/package.json +41 -0
  67. package/src/agent.ts +235 -0
  68. package/src/context.ts +82 -0
  69. package/src/create-agent.ts +237 -0
  70. package/src/extension.ts +244 -0
  71. package/src/generate.ts +943 -0
  72. package/src/index.ts +113 -0
  73. package/src/message.ts +114 -0
  74. package/src/messages.test.ts +299 -0
  75. package/src/messages.ts +423 -0
  76. package/src/model.ts +43 -0
  77. package/src/provider.ts +187 -0
  78. package/src/resolve-schema.test.ts +351 -0
  79. package/src/resolve-schema.ts +426 -0
  80. package/src/schema.ts +131 -0
  81. package/src/tool-dispatch.ts +166 -0
  82. package/src/tool-result-cache.test.ts +182 -0
  83. package/src/tool-result-cache.ts +164 -0
  84. package/src/tool.ts +110 -0
  85. package/src/util.ts +110 -0
@@ -0,0 +1,351 @@
1
+ /**
2
+ * Tests for resolveJsonSchema — ensures correct JSON Schema resolution
3
+ * from Standard Schema objects (Zod v3, Zod v4, pre-computed).
4
+ */
5
+ import { describe, it, expect } from "bun:test";
6
+ import { resolveJsonSchema, validateToolSchemas } from "./resolve-schema";
7
+ import { Schema } from "./schema";
8
+ import type { StandardSchemaV1 } from "./schema";
9
+ import type { AnyTool } from "./tool";
10
+ import { z } from "zod";
11
+
12
+ describe("resolveJsonSchema", () => {
13
+ it("resolves a Zod v3 object schema", () => {
14
+ const schema = z.object({
15
+ path: z.string().describe("File path"),
16
+ lineStart: z.number().int().min(1).optional().describe("Start line"),
17
+ });
18
+
19
+ const result = resolveJsonSchema(schema);
20
+
21
+ expect(result.type).toBe("object");
22
+ expect(result.properties).toBeDefined();
23
+ const props = result.properties as Record<string, any>;
24
+ expect(props.path).toBeDefined();
25
+ expect(props.path.type).toBe("string");
26
+ expect(props.lineStart).toBeDefined();
27
+ // Should have required array with "path" only
28
+ expect(result.required).toContain("path");
29
+ expect(result.required).not.toContain("lineStart");
30
+ });
31
+
32
+ it("strips $schema and additionalProperties", () => {
33
+ const schema = z.object({
34
+ name: z.string(),
35
+ });
36
+
37
+ const result = resolveJsonSchema(schema);
38
+
39
+ expect(result.$schema).toBeUndefined();
40
+ expect(result.additionalProperties).toBeUndefined();
41
+ });
42
+
43
+ it("sanitizes anyOf nullable patterns", () => {
44
+ // z.string().nullable() produces anyOf: [{type: "string"}, {type: "null"}]
45
+ const schema = z.object({
46
+ parentId: z.string().nullable().describe("Parent ID"),
47
+ });
48
+
49
+ const result = resolveJsonSchema(schema);
50
+ const props = result.properties as Record<string, any>;
51
+
52
+ // Should NOT have anyOf — should be flattened to {type: "string"}
53
+ expect(props.parentId.anyOf).toBeUndefined();
54
+ expect(props.parentId.type).toBe("string");
55
+ });
56
+
57
+ it("sanitizes exclusiveMinimum boolean (draft-04)", () => {
58
+ // Some converters emit {exclusiveMinimum: true, minimum: 0}
59
+ const mockSchema = {
60
+ "~standard": {
61
+ version: 1,
62
+ vendor: "test",
63
+ jsonSchema: {
64
+ type: "object",
65
+ properties: {
66
+ count: {
67
+ type: "integer",
68
+ exclusiveMinimum: true,
69
+ minimum: 0,
70
+ },
71
+ },
72
+ },
73
+ validate: (v: unknown) => ({ value: v }),
74
+ },
75
+ } as unknown as StandardSchemaV1;
76
+
77
+ const result = resolveJsonSchema(mockSchema);
78
+ const props = result.properties as Record<string, any>;
79
+
80
+ // Should convert to numeric exclusiveMinimum
81
+ expect(props.count.exclusiveMinimum).toBe(0);
82
+ expect(props.count.minimum).toBeUndefined();
83
+ });
84
+
85
+ it("reads pre-computed jsonSchema from ~standard (fast path)", () => {
86
+ const precomputed = {
87
+ "~standard": {
88
+ version: 1,
89
+ vendor: "dex",
90
+ jsonSchema: {
91
+ type: "object",
92
+ properties: {
93
+ id: { type: "string", description: "Task ID" },
94
+ },
95
+ required: ["id"],
96
+ },
97
+ validate: (v: unknown) => ({ value: v }),
98
+ },
99
+ } as unknown as StandardSchemaV1;
100
+
101
+ const result = resolveJsonSchema(precomputed);
102
+
103
+ expect(result.type).toBe("object");
104
+ const props = result.properties as Record<string, any>;
105
+ expect(props.id.type).toBe("string");
106
+ expect(props.id.description).toBe("Task ID");
107
+ expect(result.required).toEqual(["id"]);
108
+ });
109
+
110
+ it("caches results for the same schema object", () => {
111
+ const schema = z.object({ x: z.string() });
112
+
113
+ const result1 = resolveJsonSchema(schema);
114
+ const result2 = resolveJsonSchema(schema);
115
+
116
+ expect(result1).toBe(result2); // Same reference (cached)
117
+ });
118
+
119
+ it("handles nested objects", () => {
120
+ const schema = z.object({
121
+ tasks: z
122
+ .array(
123
+ z.object({
124
+ title: z.string().min(1).describe("Task title"),
125
+ priority: z.number().int().min(0).max(10).optional(),
126
+ }),
127
+ )
128
+ .min(1)
129
+ .max(100),
130
+ });
131
+
132
+ const result = resolveJsonSchema(schema);
133
+ const props = result.properties as Record<string, any>;
134
+
135
+ expect(props.tasks.type).toBe("array");
136
+ expect(props.tasks.minItems).toBe(1);
137
+ expect(props.tasks.maxItems).toBe(100);
138
+ expect(props.tasks.items.type).toBe("object");
139
+ expect(props.tasks.items.properties.title.type).toBe("string");
140
+ });
141
+
142
+ it("handles enum schemas", () => {
143
+ const schema = z.object({
144
+ status: z.enum(["pending", "done", "failed"]).describe("Task status"),
145
+ });
146
+
147
+ const result = resolveJsonSchema(schema);
148
+ const props = result.properties as Record<string, any>;
149
+
150
+ expect(props.status.enum).toEqual(["pending", "done", "failed"]);
151
+ });
152
+ });
153
+
154
+ describe("validateToolSchemas", () => {
155
+ function makeTool(name: string, params: any): AnyTool {
156
+ return {
157
+ name,
158
+ description: `Test tool ${name}`,
159
+ parameters: params,
160
+ execute: async () => "ok",
161
+ };
162
+ }
163
+
164
+ it("returns no diagnostics for valid tools", () => {
165
+ const tools = [
166
+ makeTool("read", z.object({ path: z.string() })),
167
+ makeTool("write", z.object({ path: z.string(), content: z.string() })),
168
+ ];
169
+
170
+ const diags = validateToolSchemas(tools);
171
+ expect(diags).toHaveLength(0);
172
+ });
173
+
174
+ it("returns error for tool with degenerate schema (empty properties)", () => {
175
+ const brokenSchema = {
176
+ "~standard": {
177
+ version: 1,
178
+ vendor: "broken",
179
+ validate: () => ({ value: {} }),
180
+ },
181
+ } as unknown as StandardSchemaV1;
182
+
183
+ const tools = [makeTool("broken_tool", brokenSchema)];
184
+ const diags = validateToolSchemas(tools);
185
+
186
+ expect(diags.length).toBeGreaterThan(0);
187
+ const err = diags.find((d) => d.toolName === "broken_tool");
188
+ expect(err).toBeDefined();
189
+ expect(err!.severity).toBe("error");
190
+ expect(err!.issue).toContain("no properties");
191
+ });
192
+
193
+ it("returns warning for tool with no parameters", () => {
194
+ const tools = [makeTool("no_params", undefined)];
195
+ const diags = validateToolSchemas(tools);
196
+
197
+ expect(diags).toHaveLength(1);
198
+ expect(diags[0]!.severity).toBe("warning");
199
+ expect(diags[0]!.issue).toContain("no parameters schema");
200
+ });
201
+
202
+ it("includes extensionName in diagnostics when provided", () => {
203
+ const brokenSchema = {
204
+ "~standard": {
205
+ version: 1,
206
+ vendor: "broken",
207
+ validate: () => ({ value: {} }),
208
+ },
209
+ } as unknown as StandardSchemaV1;
210
+
211
+ const tools = [makeTool("bad", brokenSchema)];
212
+ const diags = validateToolSchemas(tools, { extensionName: "my-ext" });
213
+
214
+ expect(diags[0]!.extensionName).toBe("my-ext");
215
+ });
216
+
217
+ it("passes valid nullable patterns (sanitized by resolveJsonSchema)", () => {
218
+ // After sanitization, .nullable() should be flattened — no anyOf left
219
+ const tools = [
220
+ makeTool(
221
+ "task_create",
222
+ z.object({
223
+ title: z.string(),
224
+ parentId: z.string().nullable().describe("Parent"),
225
+ }),
226
+ ),
227
+ ];
228
+
229
+ const diags = validateToolSchemas(tools);
230
+ // After sanitize strips anyOf nullable, no warnings expected
231
+ expect(diags).toHaveLength(0);
232
+ });
233
+
234
+ it("catches schema resolution errors", () => {
235
+ const throwingSchema = {
236
+ "~standard": {
237
+ version: 1,
238
+ vendor: "zod",
239
+ validate: () => ({ value: {} }),
240
+ },
241
+ // Has _zod to trigger Zod v4 path, which will fail
242
+ _zod: { def: {} },
243
+ _def: { typeName: "ZodObject" },
244
+ } as unknown as StandardSchemaV1;
245
+
246
+ const tools = [makeTool("throws", throwingSchema)];
247
+ const diags = validateToolSchemas(tools);
248
+
249
+ // Should either resolve to something or produce a diagnostic — not crash
250
+ // The exact outcome depends on whether require('zod') succeeds in test env
251
+ expect(diags).toBeDefined();
252
+ });
253
+ });
254
+
255
+ describe("Schema.fromJsonSchema", () => {
256
+ it("creates a StandardSchemaV1 from raw JSON Schema", () => {
257
+ const params = Schema.fromJsonSchema({
258
+ type: "object",
259
+ properties: {
260
+ path: { type: "string", description: "File path" },
261
+ lineStart: { type: "integer", minimum: 1 },
262
+ },
263
+ required: ["path"],
264
+ });
265
+
266
+ // Should satisfy StandardSchemaV1 interface
267
+ expect(params["~standard"].version).toBe(1);
268
+ expect(params["~standard"].vendor).toBe("dex");
269
+ expect(typeof params["~standard"].validate).toBe("function");
270
+ });
271
+
272
+ it("resolves directly via resolveJsonSchema (fast path)", () => {
273
+ const params = Schema.fromJsonSchema({
274
+ type: "object",
275
+ properties: {
276
+ name: { type: "string" },
277
+ age: { type: "integer", minimum: 0 },
278
+ },
279
+ required: ["name"],
280
+ });
281
+
282
+ const result = resolveJsonSchema(params);
283
+
284
+ expect(result.type).toBe("object");
285
+ const props = result.properties as Record<string, any>;
286
+ expect(props.name.type).toBe("string");
287
+ expect(props.age.type).toBe("integer");
288
+ expect(props.age.minimum).toBe(0);
289
+ expect(result.required).toEqual(["name"]);
290
+ });
291
+
292
+ it("passes tool schema validation", () => {
293
+ const tools: AnyTool[] = [
294
+ {
295
+ name: "my_tool",
296
+ description: "A tool",
297
+ parameters: Schema.fromJsonSchema({
298
+ type: "object",
299
+ properties: {
300
+ query: { type: "string" },
301
+ },
302
+ required: ["query"],
303
+ }),
304
+ execute: async () => "ok",
305
+ },
306
+ ];
307
+
308
+ const diags = validateToolSchemas(tools);
309
+ expect(diags).toHaveLength(0);
310
+ });
311
+
312
+ it("validate function passes through input", () => {
313
+ const params = Schema.fromJsonSchema({ type: "object", properties: {} });
314
+ const input = { foo: "bar" };
315
+ const result = params["~standard"].validate(input);
316
+ expect(result).toEqual({ value: input });
317
+ });
318
+
319
+ it("validate rejects null/undefined for object schema", () => {
320
+ const params = Schema.fromJsonSchema({ type: "object", properties: {} });
321
+ const r1 = params["~standard"].validate(undefined);
322
+ expect("issues" in r1).toBe(true);
323
+ if ("issues" in r1) {
324
+ expect(r1.issues[0]!.message).toContain("Expected an object");
325
+ }
326
+ const r2 = params["~standard"].validate(null);
327
+ expect("issues" in r2).toBe(true);
328
+ });
329
+
330
+ it("validate checks required fields", () => {
331
+ const params = Schema.fromJsonSchema({
332
+ type: "object",
333
+ properties: { name: { type: "string" }, age: { type: "number" } },
334
+ required: ["name", "age"],
335
+ });
336
+ const r1 = params["~standard"].validate({ name: "foo" });
337
+ expect("issues" in r1).toBe(true);
338
+ if ("issues" in r1) {
339
+ expect(r1.issues).toHaveLength(1);
340
+ expect(r1.issues[0]!.message).toContain('"age"');
341
+ }
342
+ const r2 = params["~standard"].validate({ name: "foo", age: 30 });
343
+ expect("value" in r2).toBe(true);
344
+ });
345
+
346
+ it("validate allows non-object schemas through without checks", () => {
347
+ const params = Schema.fromJsonSchema({ type: "string" } as any);
348
+ const result = params["~standard"].validate("hello");
349
+ expect(result).toEqual({ value: "hello" });
350
+ });
351
+ });