@agentuity/cli 0.0.49 → 0.0.50

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 (52) hide show
  1. package/dist/cli.d.ts.map +1 -1
  2. package/dist/cmd/agents/index.d.ts +2 -0
  3. package/dist/cmd/agents/index.d.ts.map +1 -0
  4. package/dist/cmd/auth/ssh/list.d.ts.map +1 -1
  5. package/dist/cmd/bundle/ast.d.ts +3 -1
  6. package/dist/cmd/bundle/ast.d.ts.map +1 -1
  7. package/dist/cmd/bundle/plugin.d.ts.map +1 -1
  8. package/dist/cmd/cloud/deployment/list.d.ts.map +1 -1
  9. package/dist/cmd/cloud/index.d.ts.map +1 -1
  10. package/dist/cmd/cloud/session/get.d.ts +2 -0
  11. package/dist/cmd/cloud/session/get.d.ts.map +1 -0
  12. package/dist/cmd/cloud/session/index.d.ts +2 -0
  13. package/dist/cmd/cloud/session/index.d.ts.map +1 -0
  14. package/dist/cmd/cloud/session/list.d.ts +2 -0
  15. package/dist/cmd/cloud/session/list.d.ts.map +1 -0
  16. package/dist/cmd/cloud/session/logs.d.ts +2 -0
  17. package/dist/cmd/cloud/session/logs.d.ts.map +1 -0
  18. package/dist/cmd/dev/agents.d.ts +2 -0
  19. package/dist/cmd/dev/agents.d.ts.map +1 -0
  20. package/dist/cmd/dev/index.d.ts.map +1 -1
  21. package/dist/cmd/dev/sync.d.ts +12 -0
  22. package/dist/cmd/dev/sync.d.ts.map +1 -0
  23. package/dist/repl.d.ts +2 -6
  24. package/dist/repl.d.ts.map +1 -1
  25. package/dist/tui.d.ts +24 -0
  26. package/dist/tui.d.ts.map +1 -1
  27. package/dist/types.d.ts +12 -0
  28. package/dist/types.d.ts.map +1 -1
  29. package/dist/utils/format.d.ts +9 -0
  30. package/dist/utils/format.d.ts.map +1 -0
  31. package/package.json +6 -3
  32. package/src/cli.ts +6 -4
  33. package/src/cmd/agents/index.ts +147 -0
  34. package/src/cmd/auth/ssh/list.ts +10 -16
  35. package/src/cmd/bundle/ast.test.ts +2 -2
  36. package/src/cmd/bundle/ast.ts +85 -17
  37. package/src/cmd/bundle/plugin.ts +24 -2
  38. package/src/cmd/cloud/deployment/list.ts +16 -22
  39. package/src/cmd/cloud/index.ts +2 -0
  40. package/src/cmd/cloud/session/get.ts +164 -0
  41. package/src/cmd/cloud/session/index.ts +11 -0
  42. package/src/cmd/cloud/session/list.ts +145 -0
  43. package/src/cmd/cloud/session/logs.ts +68 -0
  44. package/src/cmd/dev/agents.ts +122 -0
  45. package/src/cmd/dev/index.ts +101 -7
  46. package/src/cmd/dev/sync.ts +414 -0
  47. package/src/cmd/project/list.ts +1 -1
  48. package/src/cmd/project/show.ts +1 -1
  49. package/src/repl.ts +2 -12
  50. package/src/tui.ts +94 -0
  51. package/src/types.ts +13 -10
  52. package/src/utils/format.ts +17 -0
@@ -0,0 +1,414 @@
1
+ import { z } from 'zod';
2
+ import type { Logger, BuildMetadata } from '../../types';
3
+ import type { APIClient } from '../../api';
4
+
5
+ interface AgentSyncPayload {
6
+ id: string;
7
+ name: string;
8
+ identifier: string;
9
+ agentId: string;
10
+ description?: string;
11
+ version: string;
12
+ filename: string;
13
+ projectId: string;
14
+ subagents?: Array<{
15
+ id: string;
16
+ name: string;
17
+ identifier: string;
18
+ agentId: string;
19
+ description?: string;
20
+ version: string;
21
+ filename: string;
22
+ projectId: string;
23
+ }>;
24
+ }
25
+
26
+ interface EvalSyncPayload {
27
+ id: string;
28
+ name: string;
29
+ identifier: string;
30
+ evalId: string;
31
+ description?: string;
32
+ version: string;
33
+ filename: string;
34
+ projectId: string;
35
+ agentIdentifier: string;
36
+ }
37
+
38
+ interface IDevmodeSyncService {
39
+ sync(
40
+ currentMetadata: BuildMetadata,
41
+ previousMetadata: BuildMetadata | undefined,
42
+ projectId: string,
43
+ deploymentId: string
44
+ ): Promise<void>;
45
+ }
46
+
47
+ // Shared diff logic for agents
48
+ function getAgentsToSync(
49
+ currentAgents: BuildMetadata['agents'],
50
+ previousAgentIds: Set<string>,
51
+ projectId: string,
52
+ logger: Logger
53
+ ): { create: AgentSyncPayload[]; delete: string[] } {
54
+ const agentsToCreate: AgentSyncPayload[] = [];
55
+ const currentAgentIds = new Set<string>();
56
+
57
+ for (const agent of currentAgents || []) {
58
+ currentAgentIds.add(agent.id);
59
+ // If ID is not in previous, add to create
60
+ if (!previousAgentIds.has(agent.id)) {
61
+ logger.debug(
62
+ '[CLI AGENT SYNC] Preparing to create: id="%s", name="%s"',
63
+ agent.id,
64
+ agent.name
65
+ );
66
+
67
+ // Add projectId to subagents if they exist
68
+ const subagents = agent.subagents?.map((subagent) => {
69
+ return {
70
+ id: subagent.id,
71
+ name: subagent.name,
72
+ identifier: subagent.identifier,
73
+ agentId: subagent.agentId,
74
+ description: subagent.description,
75
+ version: subagent.version,
76
+ filename: subagent.filename,
77
+ projectId,
78
+ };
79
+ });
80
+
81
+ agentsToCreate.push({
82
+ id: agent.id,
83
+ name: agent.name,
84
+ identifier: agent.identifier,
85
+ agentId: agent.agentId,
86
+ description: agent.description,
87
+ version: agent.version,
88
+ filename: agent.filename,
89
+ projectId,
90
+ ...(subagents && { subagents }),
91
+ });
92
+ }
93
+ }
94
+
95
+ // If ID is in previous but not in current, add to delete
96
+ const agentsToDelete: string[] = [];
97
+ for (const previousId of previousAgentIds) {
98
+ if (!currentAgentIds.has(previousId)) {
99
+ logger.debug('[CLI AGENT SYNC] Preparing to delete: id="%s"', previousId);
100
+ agentsToDelete.push(previousId);
101
+ }
102
+ }
103
+
104
+ return { create: agentsToCreate, delete: agentsToDelete };
105
+ }
106
+
107
+ // Shared diff logic for evals
108
+ function getEvalsToSync(
109
+ currentMetadata: BuildMetadata,
110
+ previousEvalIds: Set<string>,
111
+ projectId: string,
112
+ logger: Logger
113
+ ): { create: EvalSyncPayload[]; delete: string[] } {
114
+ const evalsToCreate: EvalSyncPayload[] = [];
115
+ const currentEvalIds = new Set<string>();
116
+
117
+ for (const agent of currentMetadata.agents || []) {
118
+ if (agent.evals) {
119
+ for (const evalItem of agent.evals) {
120
+ currentEvalIds.add(evalItem.id);
121
+ // If ID is not in previous, add to create
122
+ if (!previousEvalIds.has(evalItem.id)) {
123
+ logger.debug(
124
+ '[CLI EVAL SYNC] Preparing to create: id="%s", name="%s"',
125
+ evalItem.id,
126
+ evalItem.name
127
+ );
128
+
129
+ evalsToCreate.push({
130
+ ...evalItem,
131
+ evalId: evalItem.evalId,
132
+ projectId,
133
+ agentIdentifier: agent.agentId,
134
+ });
135
+ }
136
+ }
137
+ }
138
+ }
139
+
140
+ // If ID is in previous but not in current, add to delete
141
+ const evalsToDelete: string[] = [];
142
+ for (const previousId of previousEvalIds) {
143
+ if (!currentEvalIds.has(previousId)) {
144
+ logger.debug('[CLI EVAL SYNC] Preparing to delete: id="%s"', previousId);
145
+ evalsToDelete.push(previousId);
146
+ }
147
+ }
148
+
149
+ return { create: evalsToCreate, delete: evalsToDelete };
150
+ }
151
+
152
+ class DevmodeSyncService implements IDevmodeSyncService {
153
+ constructor(
154
+ private logger: Logger,
155
+ private apiClient: APIClient
156
+ ) {}
157
+
158
+ async sync(
159
+ currentMetadata: BuildMetadata,
160
+ previousMetadata: BuildMetadata | undefined,
161
+ projectId: string,
162
+ deploymentId: string
163
+ ): Promise<void> {
164
+ // Build previous agent IDs set
165
+ const previousAgentIds = new Set<string>();
166
+ if (previousMetadata) {
167
+ this.logger.debug(
168
+ 'Previous metadata found with %d agent(s)',
169
+ previousMetadata.agents?.length ?? 0
170
+ );
171
+ for (const agent of previousMetadata.agents || []) {
172
+ previousAgentIds.add(agent.id);
173
+ }
174
+ } else {
175
+ this.logger.debug('No previous metadata, all agents will be treated as new');
176
+ }
177
+
178
+ // Build previous eval IDs set
179
+ const previousEvalIds = new Set<string>();
180
+ if (previousMetadata) {
181
+ let prevEvalCount = 0;
182
+ for (const agent of previousMetadata.agents || []) {
183
+ if (agent.evals) {
184
+ for (const evalItem of agent.evals) {
185
+ previousEvalIds.add(evalItem.id);
186
+ prevEvalCount++;
187
+ }
188
+ }
189
+ }
190
+ this.logger.debug('Previous metadata found with %d eval(s)', prevEvalCount);
191
+ } else {
192
+ this.logger.debug('No previous metadata, all evals will be treated as new');
193
+ }
194
+
195
+ const currentAgents = currentMetadata.agents || [];
196
+ this.logger.debug('Processing %d current agent(s)', currentAgents.length);
197
+
198
+ let currentEvalCount = 0;
199
+ for (const agent of currentMetadata.agents || []) {
200
+ if (agent.evals) {
201
+ currentEvalCount += agent.evals.length;
202
+ }
203
+ }
204
+ this.logger.debug('Processing %d current eval(s)', currentEvalCount);
205
+
206
+ // Get agents and evals to sync using shared diff logic
207
+ const { create: agentsToCreate, delete: agentsToDelete } = getAgentsToSync(
208
+ currentAgents,
209
+ previousAgentIds,
210
+ projectId,
211
+ this.logger
212
+ );
213
+ const { create: evalsToCreate, delete: evalsToDelete } = getEvalsToSync(
214
+ currentMetadata,
215
+ previousEvalIds,
216
+ projectId,
217
+ this.logger
218
+ );
219
+
220
+ if (agentsToCreate.length > 0 || agentsToDelete.length > 0) {
221
+ this.logger.debug(
222
+ 'Bulk syncing %d agent(s) to create, %d agent(s) to delete',
223
+ agentsToCreate.length,
224
+ agentsToDelete.length
225
+ );
226
+ }
227
+ if (evalsToCreate.length > 0 || evalsToDelete.length > 0) {
228
+ this.logger.debug(
229
+ 'Bulk syncing %d eval(s) to create, %d eval(s) to delete',
230
+ evalsToCreate.length,
231
+ evalsToDelete.length
232
+ );
233
+ }
234
+
235
+ // Sync both in parallel
236
+ try {
237
+ await Promise.all([
238
+ this.syncAgents(agentsToCreate, agentsToDelete, deploymentId),
239
+ this.syncEvals(evalsToCreate, evalsToDelete, deploymentId),
240
+ ]);
241
+
242
+ if (agentsToCreate.length > 0 || agentsToDelete.length > 0) {
243
+ this.logger.debug(
244
+ 'Successfully bulk synced %d agent(s) to create, %d agent(s) to delete',
245
+ agentsToCreate.length,
246
+ agentsToDelete.length
247
+ );
248
+ }
249
+ if (evalsToCreate.length > 0 || evalsToDelete.length > 0) {
250
+ this.logger.debug(
251
+ 'Successfully bulk synced %d eval(s) to create, %d eval(s) to delete',
252
+ evalsToCreate.length,
253
+ evalsToDelete.length
254
+ );
255
+ }
256
+ } catch (error) {
257
+ this.logger.error('Failed to bulk sync agents/evals: %s', error);
258
+ if (error instanceof Error) {
259
+ this.logger.error('Error details: %s', error.message);
260
+ }
261
+ throw error;
262
+ }
263
+ }
264
+
265
+ private async syncAgents(
266
+ agents: AgentSyncPayload[],
267
+ agentsToDelete: string[],
268
+ deploymentId: string
269
+ ): Promise<void> {
270
+ if (agents.length === 0 && agentsToDelete.length === 0) {
271
+ return;
272
+ }
273
+
274
+ const payload = {
275
+ create: agents,
276
+ delete: agentsToDelete,
277
+ deploymentId,
278
+ };
279
+ this.logger.trace(
280
+ '[CLI AGENT SYNC] Sending payload to POST /cli/devmode/agent: %s',
281
+ JSON.stringify(payload, null, 2)
282
+ );
283
+
284
+ await this.apiClient.request(
285
+ 'POST',
286
+ '/cli/devmode/agent',
287
+ z.object({ success: z.boolean() }),
288
+ payload
289
+ );
290
+ }
291
+
292
+ private async syncEvals(
293
+ evals: EvalSyncPayload[],
294
+ evalsToDelete: string[],
295
+ deploymentId: string
296
+ ): Promise<void> {
297
+ if (evals.length === 0 && evalsToDelete.length === 0) {
298
+ return;
299
+ }
300
+
301
+ const payload = {
302
+ deploymentId,
303
+ create: evals,
304
+ delete: evalsToDelete,
305
+ };
306
+ this.logger.trace(
307
+ '[CLI EVAL SYNC] Sending payload to POST /cli/devmode/eval: %s',
308
+ JSON.stringify(payload, null, 2)
309
+ );
310
+
311
+ await this.apiClient.request(
312
+ 'POST',
313
+ '/cli/devmode/eval',
314
+ z.object({ success: z.boolean() }),
315
+ payload
316
+ );
317
+ }
318
+ }
319
+
320
+ class MockDevmodeSyncService implements IDevmodeSyncService {
321
+ constructor(private logger: Logger) {}
322
+
323
+ async sync(
324
+ currentMetadata: BuildMetadata,
325
+ previousMetadata: BuildMetadata | undefined,
326
+ projectId: string,
327
+ deploymentId: string
328
+ ): Promise<void> {
329
+ // Build previous agent IDs set
330
+ this.logger.debug('Mock syncing agents and evals for deploymentId: %s', deploymentId);
331
+ const previousAgentIds = new Set<string>();
332
+ if (previousMetadata) {
333
+ for (const agent of previousMetadata.agents || []) {
334
+ previousAgentIds.add(agent.id);
335
+ }
336
+ }
337
+
338
+ // Build previous eval IDs set
339
+ const previousEvalIds = new Set<string>();
340
+ if (previousMetadata) {
341
+ for (const agent of previousMetadata.agents || []) {
342
+ if (agent.evals) {
343
+ for (const evalItem of agent.evals) {
344
+ previousEvalIds.add(evalItem.id);
345
+ }
346
+ }
347
+ }
348
+ }
349
+
350
+ // Get agents and evals to sync using shared diff logic
351
+ const { create: agentsToCreate, delete: agentsToDelete } = getAgentsToSync(
352
+ currentMetadata.agents,
353
+ previousAgentIds,
354
+ projectId,
355
+ this.logger
356
+ );
357
+ const { create: evalsToCreate, delete: evalsToDelete } = getEvalsToSync(
358
+ currentMetadata,
359
+ previousEvalIds,
360
+ projectId,
361
+ this.logger
362
+ );
363
+
364
+ // Log the requests that would be made
365
+ if (agentsToCreate.length > 0 || agentsToDelete.length > 0) {
366
+ this.logger.info(
367
+ '[MOCK] Would make request: POST /cli/devmode/agent with %d agent(s) to create, %d agent(s) to delete',
368
+ agentsToCreate.length,
369
+ agentsToDelete.length
370
+ );
371
+ this.logger.info(
372
+ '[MOCK] Request payload: %s',
373
+ JSON.stringify({ create: agentsToCreate, delete: agentsToDelete }, null, 2)
374
+ );
375
+ }
376
+
377
+ if (evalsToCreate.length > 0 || evalsToDelete.length > 0) {
378
+ this.logger.info(
379
+ '[MOCK] Would make request: POST /cli/devmode/eval with %d eval(s) to create, %d eval(s) to delete',
380
+ evalsToCreate.length,
381
+ evalsToDelete.length
382
+ );
383
+ this.logger.info(
384
+ '[MOCK] Request payload: %s',
385
+ JSON.stringify({ create: evalsToCreate, delete: evalsToDelete }, null, 2)
386
+ );
387
+ }
388
+
389
+ if (
390
+ agentsToCreate.length === 0 &&
391
+ agentsToDelete.length === 0 &&
392
+ evalsToCreate.length === 0 &&
393
+ evalsToDelete.length === 0
394
+ ) {
395
+ this.logger.info('[MOCK] No requests would be made (no changes detected)');
396
+ }
397
+ }
398
+ }
399
+
400
+ export function createDevmodeSyncService({
401
+ logger,
402
+ apiClient,
403
+ mock = false,
404
+ }: {
405
+ logger: Logger;
406
+ apiClient: APIClient;
407
+ mock?: boolean;
408
+ }): IDevmodeSyncService {
409
+ if (mock) {
410
+ return new MockDevmodeSyncService(logger);
411
+ }
412
+
413
+ return new DevmodeSyncService(logger, apiClient);
414
+ }
@@ -50,7 +50,7 @@ export const listSubcommand = createSubcommand({
50
50
  if (options.json) {
51
51
  console.log(JSON.stringify(projects, null, 2));
52
52
  } else {
53
- console.table(projects, ['id', 'name', 'orgName']);
53
+ tui.table(projects, ['id', 'name', 'orgName']);
54
54
  }
55
55
 
56
56
  return projects ?? [];
@@ -45,7 +45,7 @@ export const showSubcommand = createSubcommand({
45
45
  if (options.json) {
46
46
  console.log(JSON.stringify(project, null, 2));
47
47
  } else {
48
- console.table([project], ['id', 'orgId']);
48
+ tui.table([project], ['id', 'orgId']);
49
49
  }
50
50
 
51
51
  return {
package/src/repl.ts CHANGED
@@ -7,7 +7,6 @@ import { getDefaultConfigDir } from './config';
7
7
  import { join } from 'node:path';
8
8
  import { mkdir } from 'node:fs/promises';
9
9
  import { z } from 'zod';
10
- import { Table } from 'console-table-printer';
11
10
  import { colorize } from 'json-colorizer';
12
11
 
13
12
  /**
@@ -25,12 +24,7 @@ export interface ParsedCommand {
25
24
  /**
26
25
  * Table column definition
27
26
  */
28
- export interface TableColumn {
29
- /** Column name */
30
- name: string;
31
- /** Column alignment */
32
- alignment?: 'left' | 'right' | 'center';
33
- }
27
+ export type TableColumn = tui.TableColumn;
34
28
 
35
29
  /**
36
30
  * Context provided to command handlers
@@ -522,12 +516,8 @@ export async function createRepl(config: ReplConfig): Promise<void> {
522
516
  signal: abortController.signal,
523
517
  exit: exitRepl,
524
518
  table: (columns: TableColumn[], data: Record<string, unknown>[]) => {
525
- const table = new Table({ columns });
526
- for (const row of data) {
527
- table.addRow(row);
528
- }
529
519
  // Capture table output to buffer instead of direct stdout
530
- const tableOutput = table.render();
520
+ const tableOutput = tui.table(data, columns, { render: true }) || '';
531
521
  outputBuffer.push(...tableOutput.split('\n'));
532
522
  },
533
523
  json: (value: unknown) => {
package/src/tui.ts CHANGED
@@ -1364,3 +1364,97 @@ export function plural(count: number, singular: string, plural: string): string
1364
1364
  return plural;
1365
1365
  }
1366
1366
  }
1367
+
1368
+ /**
1369
+ * Table column definition
1370
+ */
1371
+ export interface TableColumn {
1372
+ /** Column name */
1373
+ name: string;
1374
+ /** Column alignment */
1375
+ alignment?: 'left' | 'right' | 'center';
1376
+ }
1377
+
1378
+ /**
1379
+ * Display data in a formatted table using console-table-printer
1380
+ *
1381
+ * Supports two modes:
1382
+ * 1. Simple mode: Pass data array and optional column names
1383
+ * 2. Advanced mode: Pass column configurations with custom names and alignment
1384
+ *
1385
+ * @param data - Array of data objects to display
1386
+ * @param columns - Column names or column configurations
1387
+ * @param options - Additional options
1388
+ * @returns If render=true, returns the table as a string, otherwise prints to stdout
1389
+ */
1390
+ export function table<T extends Record<string, unknown>>(
1391
+ data: T[],
1392
+ columns?: (keyof T)[] | TableColumn[],
1393
+ options?: { render?: boolean }
1394
+ ): string | void {
1395
+ // Dynamic import to avoid type errors (console-table-printer has poor typings)
1396
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
1397
+ const { Table } = require('console-table-printer') as {
1398
+ Table: new (options?: { columns?: Array<{ name: string; alignment: string }> }) => {
1399
+ addRow(row: Record<string, unknown>): void;
1400
+ printTable(): void;
1401
+ render(): string;
1402
+ };
1403
+ };
1404
+
1405
+ if (!data || data.length === 0) {
1406
+ return options?.render ? '' : undefined;
1407
+ }
1408
+
1409
+ // Determine if we're using advanced column config or simple column names
1410
+ const isAdvancedMode = columns && columns.length > 0 && typeof columns[0] === 'object';
1411
+
1412
+ let tableConfig: { columns: Array<{ name: string; alignment: string }> };
1413
+
1414
+ if (isAdvancedMode) {
1415
+ // Advanced mode: use provided column configurations
1416
+ tableConfig = {
1417
+ columns: (columns as TableColumn[]).map((col) => ({
1418
+ name: col.name,
1419
+ alignment: col.alignment || 'left',
1420
+ })),
1421
+ };
1422
+ } else {
1423
+ // Simple mode: determine column names from data or columns parameter
1424
+ const columnNames = columns
1425
+ ? (columns as (keyof T)[]).map((c) => String(c))
1426
+ : data.length > 0
1427
+ ? Object.keys(data[0])
1428
+ : [];
1429
+
1430
+ tableConfig = {
1431
+ columns: columnNames.map((name) => ({
1432
+ name,
1433
+ alignment: 'left',
1434
+ })),
1435
+ };
1436
+ }
1437
+
1438
+ const t = new Table(tableConfig);
1439
+
1440
+ // Add rows to table
1441
+ for (const row of data) {
1442
+ if (columns && !isAdvancedMode) {
1443
+ // Simple mode with column filtering
1444
+ const filtered: Record<string, unknown> = {};
1445
+ for (const col of columns as (keyof T)[]) {
1446
+ filtered[String(col)] = row[col];
1447
+ }
1448
+ t.addRow(filtered);
1449
+ } else {
1450
+ // Advanced mode or no column filtering
1451
+ t.addRow(row);
1452
+ }
1453
+ }
1454
+
1455
+ if (options?.render) {
1456
+ return t.render();
1457
+ } else {
1458
+ t.printTable();
1459
+ }
1460
+ }
package/src/types.ts CHANGED
@@ -413,21 +413,24 @@ const FileFields = {
413
413
  identifier: zod.string().describe('the folder for the file'),
414
414
  };
415
415
 
416
+ const EvalSchema = zod.object({
417
+ ...FileFields,
418
+ id: zod.string().describe('the unique calculated id for the eval'),
419
+ evalId: zod.string().describe('the unique id for eval for the project across deployments'),
420
+ name: zod.string().describe('the name of the eval'),
421
+ description: zod.string().optional().describe('the eval description'),
422
+ agentIdentifier: zod.string().describe('the identifier of the agent'),
423
+ projectId: zod.string().describe('the project id'),
424
+ });
425
+
416
426
  const BaseAgentFields = {
417
427
  ...FileFields,
418
428
  id: zod.string().describe('the unique calculated id for the agent'),
429
+ agentId: zod.string().describe('the unique id for agent for the project across deployments'),
430
+ projectId: zod.string().describe('the project id'),
419
431
  name: zod.string().describe('the name of the agent'),
420
432
  description: zod.string().optional().describe('the agent description'),
421
- evals: zod
422
- .array(
423
- zod.object({
424
- ...FileFields,
425
- name: zod.string().describe('the name of the eval'),
426
- description: zod.string().optional().describe('the eval description'),
427
- })
428
- )
429
- .optional()
430
- .describe('the evals for the agent'),
433
+ evals: zod.array(EvalSchema).optional().describe('the evals for the agent'),
431
434
  };
432
435
 
433
436
  const AgentSchema = zod.object({
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Abbreviate a string to a maximum length, adding ellipsis if truncated
3
+ */
4
+ export function abbreviate(str: string | null | undefined, maxLength = 8): string {
5
+ if (!str) return 'N/A';
6
+ if (str.length <= maxLength) return str;
7
+ return str.slice(0, maxLength) + '...';
8
+ }
9
+
10
+ /**
11
+ * Abbreviate a description (longer default length)
12
+ */
13
+ export function abbreviateDescription(str: string | null | undefined, maxLength = 40): string {
14
+ if (!str) return 'N/A';
15
+ if (str.length <= maxLength) return str;
16
+ return str.slice(0, maxLength) + '...';
17
+ }