@vertesia/common 1.4.0 → 1.4.1

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vertesia/common",
3
- "version": "1.4.0",
3
+ "version": "1.4.1",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",
@@ -27,7 +27,7 @@
27
27
  },
28
28
  "dependencies": {
29
29
  "ajv": "^8.20.0",
30
- "@llumiverse/common": "1.4.0"
30
+ "@llumiverse/common": "1.4.1"
31
31
  },
32
32
  "repository": {
33
33
  "type": "git",
package/src/apps.ts CHANGED
@@ -40,11 +40,17 @@ export interface AppUIConfig {
40
40
  */
41
41
  src: string;
42
42
  /**
43
- * The isolation strategy. If not specified it defaults to shadow
43
+ * The isolation strategy. If not specified it defaults to shadow.
44
44
  * - shadow - use Shadow DOM to fully isolate the plugin from the host.
45
- * - css - use CSS processing (like prefixing or other isolation techniques). Ligther but plugins may conflict with the host
45
+ * - css - inject the plugin's styles (minus the preflight) into the host document;
46
+ * lighter but styles may conflict with the host.
46
47
  */
47
48
  isolation?: 'shadow' | 'css';
49
+ /**
50
+ * When true the host modifies the app's css at load time to attempt to fix broken
51
+ * or missing styles. Only takes effect in css isolation mode. Defaults to false.
52
+ */
53
+ css_rebuild?: boolean;
48
54
  /**
49
55
  * Navigation items for the app's sidebar UI.
50
56
  * Only applicable for apps with UI capability in shell contexts (ie. CompositeApp shell).
@@ -1056,6 +1062,10 @@ export interface CompositeAppSwitchersOverrides {
1056
1062
 
1057
1063
  /**
1058
1064
  * Header button visibility overrides for the CompositeApp header.
1065
+ *
1066
+ * @deprecated Superseded by `CompositeAppConfig.headerMenu` (free-form header items).
1067
+ * Retained for backward compatibility and to seed the default header menu when no
1068
+ * `headerMenu` has been configured yet.
1059
1069
  */
1060
1070
  export interface CompositeAppHeaderOverrides {
1061
1071
  /** Whether to hide the App Portal button (defaults to false) */
@@ -1068,6 +1078,10 @@ export interface CompositeAppHeaderOverrides {
1068
1078
 
1069
1079
  /**
1070
1080
  * User menu overrides for the CompositeApp.
1081
+ *
1082
+ * @deprecated Superseded by the `user_menu` item in `CompositeAppConfig.headerMenu`.
1083
+ * Retained for backward compatibility and to seed the default header menu when no
1084
+ * `headerMenu` has been configured yet.
1071
1085
  */
1072
1086
  export interface CompositeAppUserMenuOverrides {
1073
1087
  /** Whether to hide the User Menu (defaults to false) */
@@ -1189,6 +1203,51 @@ export interface CompositeAppHomePlugin {
1189
1203
  appRoute?: string;
1190
1204
  }
1191
1205
 
1206
+ // ============================================================================
1207
+ // Header Menu Types
1208
+ // ============================================================================
1209
+
1210
+ /**
1211
+ * Discriminator for a header item.
1212
+ * The four built-ins (`app_portal`, `docs`, `help`, `user_menu`) seed the default
1213
+ * header and cannot be deleted (only hidden/customized); `custom` items are fully
1214
+ * user-defined buttons.
1215
+ */
1216
+ export type CompositeAppHeaderItemKind = 'app_portal' | 'docs' | 'help' | 'user_menu' | 'custom';
1217
+
1218
+ /** Where a header link opens. */
1219
+ export type CompositeAppHeaderItemTarget = '_self' | '_blank';
1220
+
1221
+ /** Stable identifiers for the built-in header items. */
1222
+ export const COMPOSITE_APP_HEADER_BUILTIN_IDS = ['app_portal', 'docs', 'help', 'user_menu'] as const;
1223
+
1224
+ /**
1225
+ * A single button in the CompositeApp header bar.
1226
+ *
1227
+ * Unlike sidebar nav-items, header items are free-form and not tied to an installed
1228
+ * app: each is a labelled, icon-bearing button linking to a route or external URL.
1229
+ * The `user_menu` item is special — it renders the account dropdown, so its `icon`,
1230
+ * `href`, and `target` are ignored.
1231
+ */
1232
+ export interface CompositeAppHeaderItem {
1233
+ /** Stable unique identifier. Built-ins use their kind as id (e.g. "app_portal"). */
1234
+ id: string;
1235
+ /** Item kind. `custom` for user-added buttons; otherwise one of the four built-ins. */
1236
+ kind: CompositeAppHeaderItemKind;
1237
+ /** Display label, used as the button tooltip / accessible name. */
1238
+ label: string;
1239
+ /** Lucide icon name or SVG content string. Ignored for `user_menu`. */
1240
+ icon?: string;
1241
+ /** Destination route ("/...") or external URL. Ignored for `user_menu`. */
1242
+ href?: string;
1243
+ /** Where to open the link (defaults to "_self"). Ignored for `user_menu`. */
1244
+ target?: CompositeAppHeaderItemTarget;
1245
+ /** When true, this item is hidden from the header. */
1246
+ hidden?: boolean;
1247
+ /** Optional access control settings for this header item. */
1248
+ permissions?: CompositeAppNavItemPermissions;
1249
+ }
1250
+
1192
1251
  /**
1193
1252
  * CompositeApp shell configuration.
1194
1253
  * This is the main configuration interface for storing CompositeApp settings.
@@ -1212,10 +1271,23 @@ export interface CompositeAppConfig {
1212
1271
  switchers?: CompositeAppSwitchersOverrides;
1213
1272
  /** Optional sidebar display overrides */
1214
1273
  sidebar?: CompositeAppSidebarOverrides;
1215
- /** Optional header button visibility overrides */
1274
+ /**
1275
+ * @deprecated Use `headerMenu` instead. Optional header button visibility overrides.
1276
+ * Still read to seed `headerMenu` defaults for configs saved before the header menu existed.
1277
+ */
1216
1278
  header?: CompositeAppHeaderOverrides;
1217
- /** Optional user menu overrides */
1279
+ /**
1280
+ * @deprecated Use the `user_menu` item in `headerMenu` instead. Optional user menu overrides.
1281
+ * Still read to seed `headerMenu` defaults for configs saved before the header menu existed.
1282
+ */
1218
1283
  userMenu?: CompositeAppUserMenuOverrides;
1284
+ /**
1285
+ * Optional free-form header menu. When present, the header renders from this ordered
1286
+ * list instead of the legacy `header`/`userMenu` flags. Built-in items (App Portal,
1287
+ * Docs, Help, User Menu) can be hidden/relabeled/re-icon'd/redirected; custom items
1288
+ * are arbitrary buttons.
1289
+ */
1290
+ headerMenu?: CompositeAppHeaderItem[];
1219
1291
  /** Optional theme overrides (e.g. disable dark mode) */
1220
1292
  theme?: CompositeAppThemeOverrides;
1221
1293
  /** Optional home page override. When set, redirects "/" to the specified app route instead of the dashboard. Send null to unset. */
@@ -91,6 +91,8 @@ export interface TextFallbackOptions {
91
91
  export interface ExecutionEnvironmentSettings {
92
92
  [key: string]: unknown;
93
93
  bucket_access_principal?: string;
94
+ /** Custom HTTP headers sent by OpenAI-compatible environments. */
95
+ default_headers?: Record<string, string>;
94
96
  }
95
97
 
96
98
  /**
package/src/query.ts CHANGED
@@ -87,6 +87,12 @@ export interface RunSearchQuery extends SimpleSearchQuery {
87
87
  model?: string;
88
88
  status?: ExecutionRunStatus;
89
89
  tags?: string[];
90
+ /**
91
+ * Tags to exclude. Runs carrying any of these tags are filtered out of the results,
92
+ * counts, and facet buckets. Combined with `tags` (which requires all of the listed
93
+ * tags) as an additional `$nin` constraint on the same field.
94
+ */
95
+ exclude_tags?: string[];
90
96
  query?: string;
91
97
  default_query_path?: string;
92
98
  parent?: string[];
package/src/refs.ts CHANGED
@@ -21,6 +21,7 @@ export interface ResourceRef {
21
21
  id: string;
22
22
  name: string;
23
23
  type: string;
24
+ email?: string;
24
25
  description?: string;
25
26
  version?: number;
26
27
  status?: string;
@@ -20,6 +20,7 @@ import type {
20
20
  InteractionRef,
21
21
  RunSource,
22
22
  } from '../interaction.js';
23
+ import type { ResourceRef } from '../refs.js';
23
24
  import type { AgentEvent } from '../workflow-analytics.js';
24
25
  import type { AgentToolApprovalMode } from './agent-approval.js';
25
26
  import type { ProcessDefinitionBody, ProcessState } from './process.js';
@@ -235,6 +236,13 @@ export interface AgentRun<TData = Record<string, unknown>, TProperties = Record<
235
236
 
236
237
  interactionRef: InteractionRef;
237
238
 
239
+ /**
240
+ * Resolved environment reference (name resolved from `config.environment` id).
241
+ * Populated by the list endpoint; may be absent on other endpoints or when the id
242
+ * cannot be resolved, in which case consumers should fall back to `config.environment`.
243
+ */
244
+ environmentRef?: ResourceRef;
245
+
238
246
  // --- Lifecycle ---
239
247
 
240
248
  /** Current status of the agent run */
@@ -48,6 +48,7 @@ export interface DSLWorkflowExecutionPayload extends WorkflowExecutionPayload<Re
48
48
  */
49
49
  export interface DSLActivityOptions {
50
50
  startToCloseTimeout?: DurationValue;
51
+ heartbeatTimeout?: DurationValue;
51
52
  scheduleToStartTimeout?: DurationValue;
52
53
  scheduleToCloseTimeout?: DurationValue;
53
54
  retry?: DSLRetryPolicy;
@@ -410,6 +410,68 @@ describe('process definition validation', () => {
410
410
  expect(result.errors).toContain('human_task node "review" is missing task');
411
411
  });
412
412
 
413
+ it('rejects human_task fields that use "id" instead of "name"', () => {
414
+ const definition = validDefinition();
415
+ definition.nodes.review.task = {
416
+ title: 'Review',
417
+ fields: [{ id: 'approved', type: 'boolean' } as unknown as { name: string; type: 'boolean' }],
418
+ };
419
+
420
+ const result = getProcessDefinitionValidationResult(definition);
421
+
422
+ expect(result.valid).toBe(false);
423
+ expect(result.errors).toContain(
424
+ 'human_task node "review" task.fields[0].name must be a non-empty string (not "id")',
425
+ );
426
+ });
427
+
428
+ it('rejects human_task fields with an unsupported type', () => {
429
+ const definition = validDefinition();
430
+ definition.nodes.review.task = {
431
+ title: 'Review',
432
+ fields: [{ name: 'approved', type: 'date' } as unknown as { name: string; type: 'string' }],
433
+ };
434
+
435
+ const result = getProcessDefinitionValidationResult(definition);
436
+
437
+ expect(result.valid).toBe(false);
438
+ expect(result.errors).toContain(
439
+ 'human_task node "review" task.fields[0].type must be one of string, number, boolean, select, text',
440
+ );
441
+ });
442
+
443
+ it('rejects select fields without options', () => {
444
+ const definition = validDefinition();
445
+ definition.nodes.review.task = {
446
+ title: 'Review',
447
+ fields: [{ name: 'decision', type: 'select' }],
448
+ };
449
+
450
+ const result = getProcessDefinitionValidationResult(definition);
451
+
452
+ expect(result.valid).toBe(false);
453
+ expect(result.errors).toContain(
454
+ 'human_task node "review" task.fields[0] of type "select" requires a non-empty options[] array',
455
+ );
456
+ });
457
+
458
+ it('accepts human_task select fields with options', () => {
459
+ const definition = validDefinition();
460
+ definition.nodes.review.task = {
461
+ title: 'Review',
462
+ fields: [
463
+ {
464
+ name: 'decision',
465
+ type: 'select',
466
+ options: ['approved', 'rejected'],
467
+ required: true,
468
+ },
469
+ ],
470
+ };
471
+
472
+ expect(() => validateProcessDefinitionBody(definition)).not.toThrow();
473
+ });
474
+
413
475
  it('rejects overly deep guard rules', () => {
414
476
  const definition = validDefinition();
415
477
  let guard: Record<string, unknown> = { var: 'approved' };
@@ -113,6 +113,24 @@ function validateNodeDefinition(
113
113
  errors.push(`human_task node "${nodeId}" task title is missing`);
114
114
  } else if (!Array.isArray(node.task.fields)) {
115
115
  errors.push(`human_task node "${nodeId}" task fields must be an array`);
116
+ } else {
117
+ const allowedTypes = ['string', 'number', 'boolean', 'select', 'text'];
118
+ (node.task.fields as unknown[]).forEach((field, i) => {
119
+ const path = `human_task node "${nodeId}" task.fields[${i}]`;
120
+ if (!isRecord(field)) {
121
+ errors.push(`${path} must be an object`);
122
+ return;
123
+ }
124
+ if (typeof field.name !== 'string' || field.name.length === 0) {
125
+ errors.push(`${path}.name must be a non-empty string (not "id")`);
126
+ }
127
+ if (typeof field.type !== 'string' || !allowedTypes.includes(field.type)) {
128
+ errors.push(`${path}.type must be one of ${allowedTypes.join(', ')}`);
129
+ }
130
+ if (field.type === 'select' && !(Array.isArray(field.options) && field.options.length > 0)) {
131
+ errors.push(`${path} of type "select" requires a non-empty options[] array`);
132
+ }
133
+ });
116
134
  }
117
135
  }
118
136
  if (node.type === 'tool') {
package/src/user.ts CHANGED
@@ -7,7 +7,7 @@ export interface UserWithAccounts extends User {
7
7
 
8
8
  export interface User {
9
9
  id: string;
10
- externalId: string;
10
+ externalId?: string;
11
11
  email: string;
12
12
  name: string;
13
13
  username?: string;