@mongoosejs/studio 0.5.1 → 0.5.3

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 (42) hide show
  1. package/backend/actions/ChatThread/streamChatMessage.js +12 -12
  2. package/backend/actions/Dashboard/getDashboard.js +29 -2
  3. package/backend/actions/Model/dropCollection.js +29 -0
  4. package/backend/actions/Model/index.js +1 -0
  5. package/backend/authorize.js +1 -0
  6. package/backend/chatAgent/agentSystemPrompt.js +22 -10
  7. package/backend/chatAgent/getAgentTools.js +19 -8
  8. package/backend/chatAgent/runChatAgent.js +17 -0
  9. package/backend/db/dashboardSchema.js +0 -32
  10. package/backend/index.js +4 -1
  11. package/backend/integrations/callLLM.js +5 -20
  12. package/backend/integrations/streamLLM.js +5 -24
  13. package/backend/sandbox/createSandbox.js +6 -0
  14. package/backend/vercel.js +3 -0
  15. package/debugging.js +11 -0
  16. package/eval/agent/cases.js +168 -0
  17. package/eval/agent/fixtures/carsharing.js +147 -0
  18. package/eval/agent/fixtures/ecommerce.js +152 -0
  19. package/eval/agent/run.js +323 -0
  20. package/frontend/index.js +2 -1
  21. package/frontend/public/app.js +581 -203
  22. package/frontend/public/tw.css +81 -0
  23. package/frontend/src/_util/recent-pages-history.js +116 -0
  24. package/frontend/src/api.js +96 -164
  25. package/frontend/src/chat/chat-message-script/chat-message-script.html +1 -6
  26. package/frontend/src/chat/chat.html +1 -1
  27. package/frontend/src/document/confirm-changes/confirm-changes.js +20 -0
  28. package/frontend/src/document/document.html +2 -2
  29. package/frontend/src/index.js +148 -1
  30. package/frontend/src/json-node/json-node.html +41 -39
  31. package/frontend/src/json-node/json-node.js +38 -0
  32. package/frontend/src/list-json/list-json.html +3 -2
  33. package/frontend/src/list-json/list-json.js +5 -0
  34. package/frontend/src/modal/modal.css +26 -1
  35. package/frontend/src/modal/modal.html +2 -2
  36. package/frontend/src/modal/modal.js +5 -1
  37. package/frontend/src/models/models.html +45 -5
  38. package/frontend/src/models/models.js +26 -0
  39. package/frontend/src/navbar/navbar.html +92 -5
  40. package/frontend/src/navbar/navbar.js +69 -1
  41. package/index.js +2 -1
  42. package/package.json +3 -2
@@ -0,0 +1,168 @@
1
+ 'use strict';
2
+
3
+ const assert = require('node:assert/strict');
4
+
5
+ module.exports = [
6
+ {
7
+ id: 'simple-1-user-count',
8
+ category: 'simple',
9
+ fixture: 'ecommerce',
10
+ prompt: 'How many users do we have in total, including deleted ones?',
11
+ assert(trace) {
12
+ assert.equal(trace.executionError, null, `script error: ${trace.executionError}`);
13
+ const got = typeof trace.output === 'number' ? trace.output : trace.output?.count;
14
+ assert.equal(got, 4, `expected 4 users, got ${JSON.stringify(trace.output)}`);
15
+ }
16
+ },
17
+ {
18
+ id: 'simple-2-most-expensive-product',
19
+ category: 'simple',
20
+ fixture: 'ecommerce',
21
+ prompt: 'What is the single most expensive product? Return an object with its name and price.',
22
+ assert(trace) {
23
+ assert.equal(trace.executionError, null, `script error: ${trace.executionError}`);
24
+ assert.equal(trace.output?.name, 'Laptop', `expected name=Laptop, got ${JSON.stringify(trace.output)}`);
25
+ assert.equal(trace.output?.price, 1200, `expected price=1200, got ${JSON.stringify(trace.output)}`);
26
+ }
27
+ },
28
+ {
29
+ id: 'simple-3-active-bookings',
30
+ category: 'simple',
31
+ fixture: 'carsharing',
32
+ prompt: 'How many bookings currently have status "active"?',
33
+ assert(trace) {
34
+ assert.equal(trace.executionError, null, `script error: ${trace.executionError}`);
35
+ const got = typeof trace.output === 'number' ? trace.output : trace.output?.count;
36
+ assert.equal(got, 2, `expected 2 active bookings, got ${JSON.stringify(trace.output)}`);
37
+ }
38
+ },
39
+ {
40
+ id: 'simple-4-list-evs',
41
+ category: 'simple',
42
+ fixture: 'carsharing',
43
+ prompt: 'List every electric vehicle in the fleet, returning an array with make and model.',
44
+ assert(trace) {
45
+ assert.equal(trace.executionError, null, `script error: ${trace.executionError}`);
46
+ const list = Array.isArray(trace.output) ? trace.output : trace.output?.vehicles;
47
+ assert.equal(list?.length, 2, `expected 2 EVs, got ${JSON.stringify(trace.output)}`);
48
+ const json = JSON.stringify(list);
49
+ assert.ok(json.includes('Tesla') && json.includes('Leaf'), `expected Tesla and Leaf, got ${json}`);
50
+ }
51
+ },
52
+ {
53
+ id: 'simple-5-orders-by-status',
54
+ category: 'simple',
55
+ fixture: 'ecommerce',
56
+ prompt: 'Show a table with the count of orders grouped by status.',
57
+ assert(trace) {
58
+ assert.equal(trace.executionError, null, `script error: ${trace.executionError}`);
59
+ assert.ok(trace.output?.$table, `expected $table, got ${JSON.stringify(trace.output)}`);
60
+ assert.equal(trace.output.$table.rows.length, 5, `expected 5 status rows, got ${trace.output.$table.rows.length}`);
61
+ const json = JSON.stringify(trace.output.$table);
62
+ for (const status of ['pending', 'paid', 'shipped', 'delivered', 'cancelled']) {
63
+ assert.ok(json.includes(status), `expected "${status}" in table, got ${json}`);
64
+ }
65
+ }
66
+ },
67
+
68
+ {
69
+ id: 'nested-1-orders-by-month',
70
+ category: 'nested',
71
+ fixture: 'ecommerce',
72
+ prompt: 'How many orders were placed each month over the past 6 months? Return a table.',
73
+ assert(trace) {
74
+ assert.equal(trace.executionError, null, `script error: ${trace.executionError}`);
75
+ assert.ok(trace.output?.$table, `expected $table, got ${JSON.stringify(trace.output)}`);
76
+ assert.ok(trace.output.$table.rows.length >= 3, `expected ≥3 month rows, got ${trace.output.$table.rows.length}`);
77
+ }
78
+ },
79
+ {
80
+ id: 'nested-2-late-returns-unpaid',
81
+ category: 'nested',
82
+ fixture: 'carsharing',
83
+ prompt: 'Return an array of bookings that were returned late (returnedAt after endAt) and have at least one unpaid charge.',
84
+ assert(trace) {
85
+ assert.equal(trace.executionError, null, `script error: ${trace.executionError}`);
86
+ assert.ok(Array.isArray(trace.output), `expected array, got ${JSON.stringify(trace.output)}`);
87
+ assert.equal(trace.output.length, 2, `expected 2 late+unpaid bookings, got ${trace.output.length}`);
88
+ }
89
+ },
90
+ {
91
+ id: 'nested-3-paid-orders-with-electronics',
92
+ category: 'nested',
93
+ fixture: 'ecommerce',
94
+ prompt: 'Return an array of paid orders that contain at least one electronics product.',
95
+ assert(trace) {
96
+ assert.equal(trace.executionError, null, `script error: ${trace.executionError}`);
97
+ assert.ok(Array.isArray(trace.output), `expected array, got ${JSON.stringify(trace.output)}`);
98
+ assert.equal(trace.output.length, 2, `expected 2 paid+electronics orders, got ${trace.output.length}`);
99
+ }
100
+ },
101
+
102
+ {
103
+ id: 'dashboard-1-top-products',
104
+ category: 'dashboard',
105
+ fixture: 'ecommerce',
106
+ prompt: 'Build a dashboard showing the top 5 products by gross revenue from paid orders.',
107
+ assert(trace) {
108
+ assert.equal(trace.executionError, null, `script error: ${trace.executionError}`);
109
+ const renderable = trace.output?.$chart || trace.output?.$table;
110
+ assert.ok(renderable, `expected $chart or $table, got ${JSON.stringify(trace.output)}`);
111
+ assert.ok(JSON.stringify(renderable).includes('Headphones'), `Headphones should be top product, got ${JSON.stringify(renderable)}`);
112
+ }
113
+ },
114
+ {
115
+ id: 'dashboard-2-bookings-per-vehicle-type',
116
+ category: 'dashboard',
117
+ fixture: 'carsharing',
118
+ prompt: 'Show a chart of total bookings broken down by vehicle type.',
119
+ assert(trace) {
120
+ assert.equal(trace.executionError, null, `script error: ${trace.executionError}`);
121
+ assert.ok(trace.output?.$chart, `expected $chart, got ${JSON.stringify(trace.output)}`);
122
+ const json = JSON.stringify(trace.output.$chart);
123
+ for (const type of ['ev', 'sedan', 'suv']) {
124
+ assert.ok(json.includes(type), `expected "${type}" in chart, got ${json}`);
125
+ }
126
+ }
127
+ },
128
+ {
129
+ id: 'dashboard-3-revenue-by-month',
130
+ category: 'dashboard',
131
+ fixture: 'ecommerce',
132
+ prompt: 'Chart total revenue per month from paid orders. Include only months that contain at least one paid order, in chronological order, using "YYYY-MM" format for the labels.',
133
+ assert(trace) {
134
+ assert.equal(trace.executionError, null, `script error: ${trace.executionError}`);
135
+ assert.ok(trace.output?.$chart, `expected $chart, got ${JSON.stringify(trace.output)}`);
136
+ assert.deepEqual(trace.output.$chart.data.labels, ['2026-02', '2026-03', '2026-04']);
137
+ assert.equal(trace.output.$chart.data.datasets.length, 1, `expected 1 dataset, got ${JSON.stringify(trace.output.$chart.data.datasets)}`);
138
+ assert.deepEqual(trace.output.$chart.data.datasets[0].data, [300, 60, 222]);
139
+ }
140
+ },
141
+
142
+ {
143
+ id: 'ambig-1-missing-user',
144
+ category: 'ambiguous',
145
+ fixture: 'carsharing',
146
+ prompt: 'Why does Jane Doe have a negative balance?',
147
+ assert(trace) {
148
+ assert.ok(
149
+ trace.toolCalls.some(t => t.toolName === 'findOne' || t.toolName === 'find'),
150
+ 'agent should look up the user before answering'
151
+ );
152
+ const lower = trace.text.toLowerCase();
153
+ assert.ok(
154
+ lower.includes('50') || lower.includes('positive') || lower.includes('not negative'),
155
+ `agent should note Jane's balance is actually positive; text was: ${trace.text.slice(0, 500)}`
156
+ );
157
+ }
158
+ },
159
+ {
160
+ id: 'ambig-2-investigate',
161
+ category: 'ambiguous',
162
+ fixture: 'ecommerce',
163
+ prompt: 'Some orders look weird. Can you investigate?',
164
+ assert(trace) {
165
+ assert.ok(trace.toolCalls.length >= 2, `expected ≥2 tool calls; saw ${trace.toolCalls.length}`);
166
+ }
167
+ }
168
+ ];
@@ -0,0 +1,147 @@
1
+ 'use strict';
2
+
3
+ const mongoose = require('mongoose');
4
+
5
+ const { Schema } = mongoose;
6
+
7
+ const userSchema = new Schema({
8
+ name: String,
9
+ email: String,
10
+ balance: Number,
11
+ joinedAt: Date
12
+ });
13
+
14
+ const vehicleSchema = new Schema({
15
+ make: String,
16
+ model: String,
17
+ type: { type: String, enum: ['sedan', 'suv', 'ev'] },
18
+ licensePlate: String,
19
+ dailyRate: Number
20
+ });
21
+
22
+ const bookingSchema = new Schema({
23
+ userId: { type: Schema.Types.ObjectId, ref: 'User' },
24
+ vehicleId: { type: Schema.Types.ObjectId, ref: 'Vehicle' },
25
+ startAt: Date,
26
+ endAt: Date,
27
+ returnedAt: Date,
28
+ status: { type: String, enum: ['active', 'completed', 'cancelled'] }
29
+ });
30
+
31
+ const chargeSchema = new Schema({
32
+ userId: { type: Schema.Types.ObjectId, ref: 'User' },
33
+ bookingId: { type: Schema.Types.ObjectId, ref: 'Booking' },
34
+ amount: Number,
35
+ paid: Boolean,
36
+ createdAt: Date
37
+ });
38
+
39
+ module.exports = {
40
+ name: 'carsharing',
41
+ defineModels(connection) {
42
+ return {
43
+ User: connection.model('User', userSchema),
44
+ Vehicle: connection.model('Vehicle', vehicleSchema),
45
+ Booking: connection.model('Booking', bookingSchema),
46
+ Charge: connection.model('Charge', chargeSchema)
47
+ };
48
+ },
49
+ async seed({ User, Vehicle, Booking, Charge }) {
50
+ const users = await User.create([
51
+ { name: 'Jane Doe', email: 'jane@example.com', balance: 50, joinedAt: new Date('2025-02-01') },
52
+ { name: 'John Smith', email: 'john@example.com', balance: -25, joinedAt: new Date('2025-03-15') },
53
+ { name: 'Alice Wonder', email: 'alice@example.com', balance: 100, joinedAt: new Date('2025-06-22') },
54
+ { name: 'Bob Lee', email: 'bob@example.com', balance: 0, joinedAt: new Date('2026-01-10') }
55
+ ]);
56
+
57
+ const vehicles = await Vehicle.create([
58
+ { make: 'Tesla', model: 'Model 3', type: 'ev', licensePlate: 'EV-001', dailyRate: 80 },
59
+ { make: 'Toyota', model: 'Camry', type: 'sedan', licensePlate: 'SD-110', dailyRate: 50 },
60
+ { make: 'Honda', model: 'CR-V', type: 'suv', licensePlate: 'SU-220', dailyRate: 70 },
61
+ { make: 'Nissan', model: 'Leaf', type: 'ev', licensePlate: 'EV-330', dailyRate: 60 }
62
+ ]);
63
+
64
+ const dayOffset = (n) => {
65
+ const d = new Date('2026-05-13T12:00:00.000Z');
66
+ d.setUTCDate(d.getUTCDate() + n);
67
+ return d;
68
+ };
69
+ const daysAgo = (n) => dayOffset(-n);
70
+
71
+ const bookings = await Booking.create([
72
+ {
73
+ userId: users[0]._id,
74
+ vehicleId: vehicles[0]._id,
75
+ startAt: daysAgo(40),
76
+ endAt: daysAgo(38),
77
+ returnedAt: daysAgo(38),
78
+ status: 'completed'
79
+ },
80
+ {
81
+ userId: users[1]._id,
82
+ vehicleId: vehicles[1]._id,
83
+ startAt: daysAgo(30),
84
+ endAt: daysAgo(28),
85
+ returnedAt: daysAgo(27),
86
+ status: 'completed'
87
+ },
88
+ {
89
+ userId: users[1]._id,
90
+ vehicleId: vehicles[2]._id,
91
+ startAt: daysAgo(20),
92
+ endAt: daysAgo(18),
93
+ returnedAt: daysAgo(15),
94
+ status: 'completed'
95
+ },
96
+ {
97
+ userId: users[2]._id,
98
+ vehicleId: vehicles[3]._id,
99
+ startAt: daysAgo(10),
100
+ endAt: daysAgo(8),
101
+ returnedAt: daysAgo(8),
102
+ status: 'completed'
103
+ },
104
+ {
105
+ userId: users[2]._id,
106
+ vehicleId: vehicles[0]._id,
107
+ startAt: daysAgo(3),
108
+ endAt: dayOffset(2),
109
+ returnedAt: null,
110
+ status: 'active'
111
+ },
112
+ {
113
+ userId: users[3]._id,
114
+ vehicleId: vehicles[1]._id,
115
+ startAt: daysAgo(1),
116
+ endAt: dayOffset(5),
117
+ returnedAt: null,
118
+ status: 'active'
119
+ },
120
+ {
121
+ userId: users[3]._id,
122
+ vehicleId: vehicles[3]._id,
123
+ startAt: daysAgo(60),
124
+ endAt: daysAgo(58),
125
+ returnedAt: null,
126
+ status: 'cancelled'
127
+ },
128
+ {
129
+ userId: users[0]._id,
130
+ vehicleId: vehicles[2]._id,
131
+ startAt: daysAgo(50),
132
+ endAt: daysAgo(48),
133
+ returnedAt: daysAgo(46),
134
+ status: 'completed'
135
+ }
136
+ ]);
137
+
138
+ await Charge.create([
139
+ { userId: users[0]._id, bookingId: bookings[0]._id, amount: 160, paid: true, createdAt: daysAgo(38) },
140
+ { userId: users[1]._id, bookingId: bookings[1]._id, amount: 100, paid: true, createdAt: daysAgo(27) },
141
+ { userId: users[1]._id, bookingId: bookings[2]._id, amount: 140, paid: false, createdAt: daysAgo(15) },
142
+ { userId: users[1]._id, bookingId: bookings[2]._id, amount: 35, paid: false, createdAt: daysAgo(15) },
143
+ { userId: users[2]._id, bookingId: bookings[3]._id, amount: 120, paid: true, createdAt: daysAgo(8) },
144
+ { userId: users[0]._id, bookingId: bookings[7]._id, amount: 140, paid: false, createdAt: daysAgo(46) }
145
+ ]);
146
+ }
147
+ };
@@ -0,0 +1,152 @@
1
+ 'use strict';
2
+
3
+ const mongoose = require('mongoose');
4
+
5
+ const { Schema } = mongoose;
6
+
7
+ const userSchema = new Schema({
8
+ name: String,
9
+ email: String,
10
+ signupDate: Date,
11
+ isDeleted: { type: Boolean, default: false }
12
+ });
13
+
14
+ const productSchema = new Schema({
15
+ name: String,
16
+ category: { type: String, enum: ['electronics', 'apparel', 'home', 'books'] },
17
+ price: Number,
18
+ inventory: Number,
19
+ createdAt: Date
20
+ });
21
+
22
+ const orderSchema = new Schema({
23
+ userId: { type: Schema.Types.ObjectId, ref: 'User' },
24
+ items: [{
25
+ productId: { type: Schema.Types.ObjectId, ref: 'Product' },
26
+ quantity: Number,
27
+ price: Number
28
+ }],
29
+ total: Number,
30
+ status: { type: String, enum: ['pending', 'paid', 'shipped', 'delivered', 'cancelled'] },
31
+ placedAt: Date,
32
+ paidAt: Date
33
+ });
34
+
35
+ module.exports = {
36
+ name: 'ecommerce',
37
+ defineModels(connection) {
38
+ return {
39
+ User: connection.model('User', userSchema),
40
+ Product: connection.model('Product', productSchema),
41
+ Order: connection.model('Order', orderSchema)
42
+ };
43
+ },
44
+ async seed({ User, Product, Order }) {
45
+ const users = await User.create([
46
+ { name: 'Alice Smith', email: 'alice@example.com', signupDate: new Date('2025-01-15'), isDeleted: false },
47
+ { name: 'Bob Jones', email: 'bob@example.com', signupDate: new Date('2025-04-22'), isDeleted: false },
48
+ { name: 'Carol Wu', email: 'carol@example.com', signupDate: new Date('2025-09-08'), isDeleted: true },
49
+ { name: 'David Park', email: 'david@example.com', signupDate: new Date('2025-12-03'), isDeleted: false }
50
+ ]);
51
+
52
+ const products = await Product.create([
53
+ { name: 'Laptop', category: 'electronics', price: 1200, inventory: 12, createdAt: new Date('2024-11-01') },
54
+ { name: 'Headphones', category: 'electronics', price: 150, inventory: 45, createdAt: new Date('2024-11-01') },
55
+ { name: 'T-Shirt', category: 'apparel', price: 25, inventory: 200, createdAt: new Date('2025-01-15') },
56
+ { name: 'Lamp', category: 'home', price: 60, inventory: 30, createdAt: new Date('2025-02-10') },
57
+ { name: 'Novel', category: 'books', price: 18, inventory: 100, createdAt: new Date('2025-03-04') }
58
+ ]);
59
+
60
+ const monthsAgo = (n) => {
61
+ const d = new Date('2026-05-13T12:00:00.000Z');
62
+ d.setUTCMonth(d.getUTCMonth() - n);
63
+ return d;
64
+ };
65
+
66
+ await Order.create([
67
+ {
68
+ userId: users[0]._id,
69
+ items: [{ productId: products[0]._id, quantity: 1, price: 1200 }],
70
+ total: 1200,
71
+ status: 'delivered',
72
+ placedAt: monthsAgo(5),
73
+ paidAt: monthsAgo(5)
74
+ },
75
+ {
76
+ userId: users[0]._id,
77
+ items: [{ productId: products[1]._id, quantity: 2, price: 150 }],
78
+ total: 300,
79
+ status: 'paid',
80
+ placedAt: monthsAgo(3),
81
+ paidAt: monthsAgo(3)
82
+ },
83
+ {
84
+ userId: users[1]._id,
85
+ items: [
86
+ { productId: products[2]._id, quantity: 3, price: 25 },
87
+ { productId: products[4]._id, quantity: 1, price: 18 }
88
+ ],
89
+ total: 93,
90
+ status: 'shipped',
91
+ placedAt: monthsAgo(4),
92
+ paidAt: monthsAgo(4)
93
+ },
94
+ {
95
+ userId: users[1]._id,
96
+ items: [{ productId: products[3]._id, quantity: 1, price: 60 }],
97
+ total: 60,
98
+ status: 'paid',
99
+ placedAt: monthsAgo(2),
100
+ paidAt: monthsAgo(2)
101
+ },
102
+ {
103
+ userId: users[3]._id,
104
+ items: [{ productId: products[0]._id, quantity: 1, price: 1200 }],
105
+ total: 1200,
106
+ status: 'cancelled',
107
+ placedAt: monthsAgo(2),
108
+ paidAt: null
109
+ },
110
+ {
111
+ userId: users[3]._id,
112
+ items: [{ productId: products[1]._id, quantity: 1, price: 150 }],
113
+ total: 150,
114
+ status: 'paid',
115
+ placedAt: monthsAgo(1),
116
+ paidAt: monthsAgo(1)
117
+ },
118
+ {
119
+ userId: users[1]._id,
120
+ items: [{ productId: products[4]._id, quantity: 4, price: 18 }],
121
+ total: 72,
122
+ status: 'paid',
123
+ placedAt: monthsAgo(1),
124
+ paidAt: monthsAgo(1)
125
+ },
126
+ {
127
+ userId: users[3]._id,
128
+ items: [{ productId: products[2]._id, quantity: 2, price: 25 }],
129
+ total: 50,
130
+ status: 'pending',
131
+ placedAt: monthsAgo(0),
132
+ paidAt: null
133
+ },
134
+ {
135
+ userId: users[0]._id,
136
+ items: [{ productId: products[3]._id, quantity: 2, price: 60 }],
137
+ total: 120,
138
+ status: 'cancelled',
139
+ placedAt: monthsAgo(6),
140
+ paidAt: null
141
+ },
142
+ {
143
+ userId: users[1]._id,
144
+ items: [{ productId: products[0]._id, quantity: 1, price: 1200 }],
145
+ total: 1200,
146
+ status: 'delivered',
147
+ placedAt: monthsAgo(4),
148
+ paidAt: monthsAgo(4)
149
+ }
150
+ ]);
151
+ }
152
+ };