@vertesia/common 1.5.0-dev.20260717.131047Z → 1.5.0-dev.20260725.083715Z

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 (123) hide show
  1. package/lib/apps.d.ts +184 -48
  2. package/lib/apps.d.ts.map +1 -1
  3. package/lib/apps.js +21 -9
  4. package/lib/apps.js.map +1 -1
  5. package/lib/audit-trail.d.ts +61 -1
  6. package/lib/audit-trail.d.ts.map +1 -1
  7. package/lib/audit-trail.js +15 -0
  8. package/lib/audit-trail.js.map +1 -1
  9. package/lib/environment.d.ts +2 -0
  10. package/lib/environment.d.ts.map +1 -1
  11. package/lib/environment.js.map +1 -1
  12. package/lib/index.d.ts +7 -0
  13. package/lib/index.d.ts.map +1 -1
  14. package/lib/index.js +7 -0
  15. package/lib/index.js.map +1 -1
  16. package/lib/interaction.d.ts +77 -1
  17. package/lib/interaction.d.ts.map +1 -1
  18. package/lib/interaction.js +48 -0
  19. package/lib/interaction.js.map +1 -1
  20. package/lib/platform-event.d.ts +37 -3
  21. package/lib/platform-event.d.ts.map +1 -1
  22. package/lib/platform-event.js.map +1 -1
  23. package/lib/project.d.ts +119 -20
  24. package/lib/project.d.ts.map +1 -1
  25. package/lib/project.js +65 -0
  26. package/lib/project.js.map +1 -1
  27. package/lib/query.d.ts +6 -0
  28. package/lib/query.d.ts.map +1 -1
  29. package/lib/refs.d.ts +1 -0
  30. package/lib/refs.d.ts.map +1 -1
  31. package/lib/schema-for-extraction.d.ts +21 -0
  32. package/lib/schema-for-extraction.d.ts.map +1 -0
  33. package/lib/schema-for-extraction.js +205 -0
  34. package/lib/schema-for-extraction.js.map +1 -0
  35. package/lib/store/agent-run.d.ts +23 -1
  36. package/lib/store/agent-run.d.ts.map +1 -1
  37. package/lib/store/conversation-state.d.ts +3 -1
  38. package/lib/store/conversation-state.d.ts.map +1 -1
  39. package/lib/store/conversation-state.js.map +1 -1
  40. package/lib/store/doc-analyzer.d.ts +10 -67
  41. package/lib/store/doc-analyzer.d.ts.map +1 -1
  42. package/lib/store/dsl-workflow.d.ts +1 -0
  43. package/lib/store/dsl-workflow.d.ts.map +1 -1
  44. package/lib/store/dsl-workflow.js.map +1 -1
  45. package/lib/store/grounded-extraction.d.ts +146 -0
  46. package/lib/store/grounded-extraction.d.ts.map +1 -0
  47. package/lib/store/grounded-extraction.js +8 -0
  48. package/lib/store/grounded-extraction.js.map +1 -0
  49. package/lib/store/index.d.ts +1 -0
  50. package/lib/store/index.d.ts.map +1 -1
  51. package/lib/store/index.js +1 -0
  52. package/lib/store/index.js.map +1 -1
  53. package/lib/store/store.d.ts +314 -1
  54. package/lib/store/store.d.ts.map +1 -1
  55. package/lib/store/store.js +451 -0
  56. package/lib/store/store.js.map +1 -1
  57. package/lib/store/workflow.d.ts +8 -1
  58. package/lib/store/workflow.d.ts.map +1 -1
  59. package/lib/store/workflow.js +5 -0
  60. package/lib/store/workflow.js.map +1 -1
  61. package/lib/user.d.ts +14 -0
  62. package/lib/user.d.ts.map +1 -1
  63. package/lib/user.js +31 -0
  64. package/lib/user.js.map +1 -1
  65. package/lib/vertesia-common.js +2 -2
  66. package/lib/vertesia-common.js.map +1 -1
  67. package/lib/view-configuration-validation.d.ts +15 -0
  68. package/lib/view-configuration-validation.d.ts.map +1 -0
  69. package/lib/view-configuration-validation.js +63 -0
  70. package/lib/view-configuration-validation.js.map +1 -0
  71. package/lib/view-query-validation.d.ts +13 -0
  72. package/lib/view-query-validation.d.ts.map +1 -0
  73. package/lib/view-query-validation.js +266 -0
  74. package/lib/view-query-validation.js.map +1 -0
  75. package/lib/view-validation-helpers.d.ts +15 -0
  76. package/lib/view-validation-helpers.d.ts.map +1 -0
  77. package/lib/view-validation-helpers.js +25 -0
  78. package/lib/view-validation-helpers.js.map +1 -0
  79. package/lib/views-schema.d.ts +1992 -0
  80. package/lib/views-schema.d.ts.map +1 -0
  81. package/lib/views-schema.js +674 -0
  82. package/lib/views-schema.js.map +1 -0
  83. package/lib/views-validation.d.ts +21 -0
  84. package/lib/views-validation.d.ts.map +1 -0
  85. package/lib/views-validation.js +164 -0
  86. package/lib/views-validation.js.map +1 -0
  87. package/lib/views.d.ts +381 -0
  88. package/lib/views.d.ts.map +1 -0
  89. package/lib/views.js +41 -0
  90. package/lib/views.js.map +1 -0
  91. package/package.json +5 -5
  92. package/src/agent-resources.test.ts +100 -0
  93. package/src/apps.test.ts +9 -1
  94. package/src/apps.ts +220 -73
  95. package/src/audit-trail.ts +83 -0
  96. package/src/environment.ts +2 -0
  97. package/src/index.ts +12 -0
  98. package/src/interaction.ts +135 -1
  99. package/src/platform-event.ts +39 -2
  100. package/src/project.test.ts +44 -0
  101. package/src/project.ts +205 -22
  102. package/src/query.ts +6 -0
  103. package/src/refs.ts +1 -0
  104. package/src/schema-for-extraction.test.ts +191 -0
  105. package/src/schema-for-extraction.ts +231 -0
  106. package/src/store/agent-run.ts +29 -0
  107. package/src/store/content-type-editing.test.ts +17 -0
  108. package/src/store/conversation-state.ts +4 -1
  109. package/src/store/doc-analyzer.ts +10 -76
  110. package/src/store/dsl-workflow.ts +1 -0
  111. package/src/store/grounded-extraction.ts +154 -0
  112. package/src/store/index.ts +1 -0
  113. package/src/store/store.ts +800 -1
  114. package/src/store/workflow.ts +12 -0
  115. package/src/user.ts +46 -0
  116. package/src/view-configuration-validation.ts +74 -0
  117. package/src/view-query-validation.test.ts +21 -0
  118. package/src/view-query-validation.ts +319 -0
  119. package/src/view-validation-helpers.ts +28 -0
  120. package/src/views-schema.test.ts +364 -0
  121. package/src/views-schema.ts +689 -0
  122. package/src/views-validation.ts +234 -0
  123. package/src/views.ts +484 -0
@@ -717,6 +717,21 @@ export {
717
717
  } from './email.js';
718
718
  // ================= end user communication channels ====================
719
719
 
720
+ /**
721
+ * A tool invocation executed before the first model turn of a conversation.
722
+ * Results are injected into the initial context so the agent starts with them in hand.
723
+ */
724
+ export interface InitialToolCall {
725
+ /** Stable identifier used to make initialization replay-safe. */
726
+ id: string;
727
+ /** Read-only builtin activity tool name. Skills are configured separately through initial_skills. */
728
+ tool: string;
729
+ /** Tool input parameters. */
730
+ input?: Record<string, unknown>;
731
+ /** Whether a failed initialization call aborts the conversation start. */
732
+ on_error?: 'fail' | 'continue';
733
+ }
734
+
720
735
  export interface AsyncConversationExecutionPayload extends AsyncExecutionPayloadBase {
721
736
  type: 'conversation';
722
737
 
@@ -735,6 +750,28 @@ export interface AsyncConversationExecutionPayload extends AsyncExecutionPayload
735
750
  */
736
751
  tool_names?: string[];
737
752
 
753
+ /**
754
+ * Builtin system skills to activate at conversation start. Their related tools are
755
+ * exposed from the first turn and their instructions are injected into the initial
756
+ * context, replacing the learn_<skill> round-trip.
757
+ */
758
+ initial_skills?: string[];
759
+
760
+ /**
761
+ * Tool calls executed before the first model turn. Results are injected into the
762
+ * initial context. These run sequentially with the caller's authority before the
763
+ * first model turn. Only a bounded set of read/hydration tools is accepted.
764
+ */
765
+ initial_tool_calls?: InitialToolCall[];
766
+
767
+ /**
768
+ * Hard denylist of tool names for this conversation. Excluded tools are never
769
+ * exposed to the model and are refused at execution time, even when a skill or
770
+ * tool refresh would otherwise unlock them. Takes precedence over tool_names,
771
+ * initial_skills, and skill-based tool activation.
772
+ */
773
+ excluded_tools?: string[];
774
+
738
775
  /**
739
776
  * The maximum number of iterations in case of a conversation. If <=0 the default of 20 will be used.
740
777
  */
@@ -973,6 +1010,52 @@ interface ResumeConversationPayload {
973
1010
  asyncCompletion?: AsyncCompletionOptions;
974
1011
  }
975
1012
 
1013
+ /**
1014
+ * The kinds of Vertesia resource an agent tool can report having created, updated, or deleted.
1015
+ * Restricted to resources that have a real detail route to navigate to — do not emit a reference
1016
+ * for a mutation with no meaningful navigation target. Add new kinds only once their route exists.
1017
+ */
1018
+ export type AgentResourceType =
1019
+ | 'document'
1020
+ | 'collection'
1021
+ | 'content_type'
1022
+ | 'interaction'
1023
+ | 'prompt'
1024
+ | 'agent'
1025
+ | 'workflow'
1026
+ | 'process'
1027
+ | 'process_run'
1028
+ | 'interaction_run'
1029
+ | 'view';
1030
+
1031
+ export type AgentResourceAction = 'created' | 'updated' | 'deleted';
1032
+
1033
+ /**
1034
+ * A navigable reference to a resource an agent tool mutated. Tools return these as tool-result
1035
+ * metadata (see {@link ToolResultMeta.resources}); the conversation runtime promotes them onto the
1036
+ * tool's completed lifecycle message so the UI can render deterministic deep links and an
1037
+ * end-of-turn "resources changed" summary — independent of any link the model writes in prose.
1038
+ */
1039
+ export interface AgentResourceReference {
1040
+ type: AgentResourceType;
1041
+ /** The resource id used to build its detail route. */
1042
+ id: string;
1043
+ /** Human-readable label captured at mutation time (e.g. the document name). */
1044
+ label: string;
1045
+ action: AgentResourceAction;
1046
+ /** Set when the mutation produced a new revision, enabling a "view changes" affordance. */
1047
+ revision_id?: string;
1048
+ }
1049
+
1050
+ /**
1051
+ * Metadata a tool executor may attach to its result. Kept as an open record for forward
1052
+ * compatibility while typing the fields the runtime interprets.
1053
+ */
1054
+ export interface ToolResultMeta extends Record<string, unknown> {
1055
+ /** Resources this tool created/updated/deleted, surfaced as deep links in the UI. */
1056
+ resources?: AgentResourceReference[];
1057
+ }
1058
+
976
1059
  export interface ToolResultContent {
977
1060
  content: string;
978
1061
  /**
@@ -992,7 +1075,54 @@ export interface ToolResultContent {
992
1075
  /**
993
1076
  * Can contain metadata returned by the tool executor.
994
1077
  */
995
- meta?: Record<string, unknown>;
1078
+ meta?: ToolResultMeta;
1079
+ }
1080
+
1081
+ const AGENT_RESOURCE_TYPES: readonly AgentResourceType[] = [
1082
+ 'document',
1083
+ 'collection',
1084
+ 'content_type',
1085
+ 'interaction',
1086
+ 'prompt',
1087
+ 'agent',
1088
+ 'workflow',
1089
+ 'process',
1090
+ 'process_run',
1091
+ 'interaction_run',
1092
+ 'view',
1093
+ ];
1094
+
1095
+ const AGENT_RESOURCE_ACTIONS: readonly AgentResourceAction[] = ['created', 'updated', 'deleted'];
1096
+
1097
+ /**
1098
+ * Validate and normalize an untrusted value into a clean list of resource references. References
1099
+ * cross the wire and may originate from external/MCP tools, so malformed entries are dropped
1100
+ * rather than throwing, and an empty/absent label falls back to the id.
1101
+ */
1102
+ export function normalizeAgentResources(value: unknown): AgentResourceReference[] {
1103
+ if (!Array.isArray(value)) return [];
1104
+ const result: AgentResourceReference[] = [];
1105
+ for (const entry of value) {
1106
+ if (!entry || typeof entry !== 'object') continue;
1107
+ const ref = entry as Record<string, unknown>;
1108
+ const { type, id, label, action, revision_id } = ref;
1109
+ if (typeof type !== 'string' || !AGENT_RESOURCE_TYPES.includes(type as AgentResourceType)) continue;
1110
+ if (typeof id !== 'string' || id.length === 0) continue;
1111
+ if (typeof action !== 'string' || !AGENT_RESOURCE_ACTIONS.includes(action as AgentResourceAction)) continue;
1112
+ result.push({
1113
+ type: type as AgentResourceType,
1114
+ id,
1115
+ label: typeof label === 'string' && label.length > 0 ? label : id,
1116
+ action: action as AgentResourceAction,
1117
+ ...(typeof revision_id === 'string' && revision_id.length > 0 ? { revision_id } : {}),
1118
+ });
1119
+ }
1120
+ return result;
1121
+ }
1122
+
1123
+ /** Extract the normalized resource references a tool declared in its result metadata. */
1124
+ export function getResourcesFromToolResult(result: Pick<ToolResultContent, 'meta'>): AgentResourceReference[] {
1125
+ return normalizeAgentResources(result.meta?.resources);
996
1126
  }
997
1127
 
998
1128
  export interface ToolResult extends ToolResultContent {
@@ -1196,6 +1326,10 @@ export interface InteractionExecutionConfiguration {
1196
1326
  run_data?: RunDataStorageLevel;
1197
1327
  configMode?: ConfigModes;
1198
1328
  model_options?: ModelOptions;
1329
+ /** Stable provider-side routing key for automatic prompt caching. */
1330
+ prompt_cache_key?: string;
1331
+ /** Put the result schema after the cached prefix; Vertesia still validates the returned JSON against it. */
1332
+ prompt_cache_schema_suffix?: boolean;
1199
1333
  /** Per-run HTTP timeouts for upstream LLM-provider calls. */
1200
1334
  http_timeout?: HttpTimeoutOptions;
1201
1335
  }
@@ -3,6 +3,7 @@ import type { ConversationVisibility, InteractionExecutionConfiguration } from '
3
3
  import type { SystemRoles } from './project.js';
4
4
  import type {
5
5
  AgentRunStatus,
6
+ GroundedVerificationBreakdown,
6
7
  JsonLogicRule,
7
8
  ProcessDefinitionBody,
8
9
  ProcessRunType,
@@ -81,12 +82,44 @@ export interface PlatformEvent extends EventRef {
81
82
  */
82
83
  export type WorkflowLifecycleAction = 'workflow_completed' | 'workflow_failed';
83
84
 
85
+ export interface DocumentProcessingModelUsage {
86
+ role: 'extraction' | 'review';
87
+ run_id: string;
88
+ model?: string;
89
+ environment_id?: string;
90
+ provider?: string;
91
+ }
92
+
93
+ /** Compact, content-free operational summary emitted after grounded IDP completes. */
94
+ export interface DocumentProcessedEventData {
95
+ schema_version: 1;
96
+ pipeline: 'grounded_extraction';
97
+ object_id: string;
98
+ page_count: number;
99
+ ocr_page_count: number;
100
+ vision_page_count: number;
101
+ property_count: number;
102
+ citation_count: number;
103
+ verification: GroundedVerificationBreakdown;
104
+ result_path: string;
105
+ confidence?: number;
106
+ coverage_min?: number;
107
+ hardness?: number;
108
+ escalated?: boolean;
109
+ reviewed?: boolean;
110
+ review_issue_count?: number;
111
+ verdict?: 'good_to_go' | 'needs_review';
112
+ verdict_reason?: string;
113
+ models_used?: DocumentProcessingModelUsage[];
114
+ review_agent_run_id?: string;
115
+ }
116
+
84
117
  /**
85
118
  * Resource flavor of a workflow lifecycle event, derived from the Temporal workflow type:
86
119
  * ExecuteConversationWorkflow -> agent_run, ExecuteProcessWorkflow -> process_run,
87
- * anything else -> workflow_run.
120
+ * document-scoped workflows may use content_object, and anything else -> workflow_run.
88
121
  */
89
- export type WorkflowLifecycleResourceType = 'workflow_run' | 'agent_run' | 'process_run';
122
+ export type WorkflowLifecycleResourceType = 'workflow_run' | 'agent_run' | 'process_run' | 'content_object';
90
123
 
91
124
  /**
92
125
  * Body of POST /internal/events/publish (zeno-server, workload-identity gated). Sent by Temporal
@@ -98,6 +131,8 @@ export interface PublishWorkflowLifecycleEventRequest {
98
131
  project_id: string;
99
132
  action: WorkflowLifecycleAction;
100
133
  resource_type: WorkflowLifecycleResourceType;
134
+ /** Domain resource the workflow acted on. Defaults to workflow_id for workflow-scoped events. */
135
+ resource_id?: string;
101
136
  workflow_id: string;
102
137
  workflow_run_id: string;
103
138
  workflow_type: string;
@@ -110,6 +145,8 @@ export interface PublishWorkflowLifecycleEventRequest {
110
145
  error?: string;
111
146
  /** EventRef of the event that started the workflow (payload.vars.event_ref), if any. */
112
147
  caused_by?: EventRef;
148
+ /** Optional IDP outcome published as a separate, subscribable content event. */
149
+ document_processed?: DocumentProcessedEventData;
113
150
  }
114
151
 
115
152
  /**
@@ -0,0 +1,44 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { validateProjectSearchPropertyMappings } from './project.js';
3
+
4
+ describe('validateProjectSearchPropertyMappings', () => {
5
+ it('accepts supported leaf mappings on relative property paths', () => {
6
+ expect(
7
+ validateProjectSearchPropertyMappings({
8
+ order_total: { type: 'double', ignore_malformed: true },
9
+ release_date: {
10
+ type: 'date',
11
+ format: 'strict_date_optional_time||yyyy-MM-dd',
12
+ ignore_malformed: true,
13
+ },
14
+ 'customer.account_number': { type: 'keyword', ignore_above: 128 },
15
+ }),
16
+ ).toEqual([]);
17
+ });
18
+
19
+ it('rejects unsupported mapping options and types', () => {
20
+ const issues = validateProjectSearchPropertyMappings({
21
+ order_total: { type: 'scaled_float', scaling_factor: 100 },
22
+ release_date: { type: 'keyword', format: 'yyyy-MM-dd' },
23
+ customer: { type: 'keyword', ignore_malformed: true },
24
+ });
25
+
26
+ expect(issues).toEqual(
27
+ expect.arrayContaining([
28
+ expect.stringContaining('unsupported option(s): scaling_factor'),
29
+ expect.stringContaining('type must be one of'),
30
+ expect.stringContaining('format is supported only for date mappings'),
31
+ expect.stringContaining('ignore_malformed is supported only for long, double, and date mappings'),
32
+ ]),
33
+ );
34
+ });
35
+
36
+ it('allows parent and child leaf mappings for subobjects:false indexes', () => {
37
+ expect(
38
+ validateProjectSearchPropertyMappings({
39
+ customer: { type: 'keyword' },
40
+ 'customer.name': { type: 'keyword' },
41
+ }),
42
+ ).toEqual([]);
43
+ });
44
+ });
package/src/project.ts CHANGED
@@ -1,7 +1,9 @@
1
1
  import type { JSONSchemaType } from 'ajv';
2
2
  import type { SupportedIntegrations } from './integrations.js';
3
+ import type { ContentTypeIntakePolicy, IntakeVisionDetail, IntakeVisionProfileSettings } from './store/store.js';
3
4
  import type { WorkflowRunStatus } from './store/workflow.js';
4
5
  import type { AccountRef } from './user.js';
6
+ import { ELASTICSEARCH_FIELD_PATH_PATTERN } from './view-validation-helpers.js';
5
7
 
6
8
  export interface ICreateProjectPayload {
7
9
  name: string;
@@ -125,6 +127,7 @@ export const SYSTEM_INTERACTION_CATEGORIES: Record<string, SystemInteractionCate
125
127
  Mediator: SystemInteractionCategory.non_applicable,
126
128
  AnalyzeConversation: SystemInteractionCategory.analysis,
127
129
  GetAgentConversationTopic: SystemInteractionCategory.analysis,
130
+ ContentSearchAgent: SystemInteractionCategory.analysis,
128
131
  StudioAssistant: SystemInteractionCategory.agent,
129
132
  };
130
133
 
@@ -269,7 +272,70 @@ export const BrowserUseProjectConfigurationSchema: JSONSchemaType<BrowserUseProj
269
272
  export type ProjectSearchTier = 'standard' | 'performance';
270
273
  export type ElasticsearchBackend = 'serverless' | 'hosted';
271
274
 
275
+ /**
276
+ * Fast pre-conversion type identification (the "sniff") for untyped documents.
277
+ * The sniff classifies a document from cheap local evidence (first/last page text,
278
+ * a low-res first-page image, office docProps) BEFORE any conversion, so a
279
+ * high-confidence match can apply the type's intake policy — including skipping
280
+ * conversion — without paying for it first.
281
+ */
282
+ export interface ProjectIntakeSniffConfiguration {
283
+ /**
284
+ * Enable the pre-conversion sniff for untyped documents. Defaults to true.
285
+ * Can be overridden per run with the `sniffEnabled` workflow var.
286
+ */
287
+ enabled?: boolean;
288
+
289
+ /**
290
+ * Confidence at or above which the sniffed type is committed and its full policy applied
291
+ * (including conversion-skip). 0..1, defaults to 0.85.
292
+ */
293
+ high_confidence?: number;
294
+
295
+ /**
296
+ * Confidence at or above which the sniffed type is treated as provisional: the document
297
+ * still converts and the post-conversion selector confirms on neutral evidence.
298
+ * 0..1, defaults to 0.6. Below this the sniff result is advisory provenance only.
299
+ */
300
+ medium_confidence?: number;
301
+
302
+ /**
303
+ * Minimum page count for the sniff LLM call. Below this, conversion is cheap and full
304
+ * converted text is better selection evidence, so intake uses the standard
305
+ * convert-then-select path. Documents with unknown page counts are sniffed.
306
+ * Defaults to 5; 0 means always sniff.
307
+ */
308
+ min_pages?: number;
309
+ }
310
+
272
311
  export interface ProjectIntakeConfiguration {
312
+ /**
313
+ * Master switch for the standard intake pipeline. When false, StandardIntake exits as a
314
+ * no-op WITHOUT touching object status (objects stay in `created`, identifiable as
315
+ * unprocessed). Defaults to true.
316
+ */
317
+ enabled?: boolean;
318
+
319
+ /**
320
+ * Fast pre-conversion type identification for untyped documents. Absent means enabled
321
+ * with platform default thresholds.
322
+ */
323
+ sniff?: ProjectIntakeSniffConfiguration;
324
+
325
+ /**
326
+ * Project-level intake policy defaults. Same shape as the per-content-type policy; a
327
+ * type's `intake` block wins field-by-field over these defaults, which in turn win over
328
+ * the legacy flat fields below. `identification` is type-specific and ignored here.
329
+ */
330
+ default_policy?: ContentTypeIntakePolicy;
331
+
332
+ /**
333
+ * Project overrides for the platform vision detail profiles used by intake visual
334
+ * extraction (`low`/`standard`/`high`). Partial: omitted profiles or fields inherit the
335
+ * platform defaults. Types reference detail NAMES only; the profile settings live here.
336
+ */
337
+ vision_profiles?: Partial<Record<IntakeVisionDetail, Partial<IntakeVisionProfileSettings>>>;
338
+
273
339
  /**
274
340
  * Generate table-of-content sections during standard document intake.
275
341
  * Defaults to false.
@@ -331,28 +397,7 @@ export interface ProjectConfiguration {
331
397
  * Indexing configuration for this project.
332
398
  * Controls whether indexing and querying are enabled at the project level.
333
399
  */
334
- indexing?: {
335
- /**
336
- * Enable indexing for content objects in this project.
337
- * When enabled, content changes trigger indexing workflows.
338
- * Defaults to true - indexing is always on when ES infrastructure is available.
339
- */
340
- enabled?: boolean;
341
-
342
- /**
343
- * Search tier for this project.
344
- * standard uses the regional hosted Elasticsearch deployment.
345
- * performance uses the regional serverless Elasticsearch project.
346
- * Defaults to standard when omitted.
347
- */
348
- search_tier?: ProjectSearchTier;
349
-
350
- /**
351
- * Elasticsearch backend override for this project.
352
- * Prefer search_tier for project configuration unless an explicit backend override is needed.
353
- */
354
- backend?: ElasticsearchBackend;
355
- };
400
+ indexing?: ProjectIndexingConfiguration;
356
401
 
357
402
  /**
358
403
  * Standard content intake behavior.
@@ -382,6 +427,142 @@ export interface ProjectConfiguration {
382
427
  pdf_template_object_id?: string;
383
428
  }
384
429
 
430
+ /**
431
+ * Elasticsearch field types that may be explicitly assigned to content-object
432
+ * properties. Paths are relative to the object's `properties` field.
433
+ */
434
+ export type ProjectSearchPropertyType = 'keyword' | 'text' | 'boolean' | 'long' | 'double' | 'date';
435
+
436
+ /**
437
+ * Explicit search mapping for one content-object property.
438
+ *
439
+ * Changing a mapping requires a full reindex. Existing Elasticsearch fields
440
+ * cannot change type in place.
441
+ */
442
+ export interface ProjectSearchPropertyMapping {
443
+ type: ProjectSearchPropertyType;
444
+
445
+ /** Elasticsearch date format. Valid only when type is `date`. */
446
+ format?: string;
447
+
448
+ /** Maximum indexed string length. Valid only when type is `keyword`. */
449
+ ignore_above?: number;
450
+
451
+ /**
452
+ * Skip malformed values instead of rejecting the whole document. Valid only
453
+ * for long, double, and date mappings.
454
+ */
455
+ ignore_malformed?: boolean;
456
+ }
457
+
458
+ export const PROJECT_SEARCH_PROPERTY_TYPES: readonly ProjectSearchPropertyType[] = [
459
+ 'keyword',
460
+ 'text',
461
+ 'boolean',
462
+ 'long',
463
+ 'double',
464
+ 'date',
465
+ ];
466
+
467
+ const MAX_PROJECT_SEARCH_PROPERTY_MAPPINGS = 200;
468
+ const MAX_KEYWORD_IGNORE_ABOVE = 8191;
469
+
470
+ /**
471
+ * Validate property mappings at API and index-creation boundaries.
472
+ *
473
+ * Returns user-facing issue strings instead of throwing so callers can map the
474
+ * result to the error type appropriate for their boundary.
475
+ */
476
+ export function validateProjectSearchPropertyMappings(value: unknown): string[] {
477
+ if (value === undefined) return [];
478
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
479
+ return ['indexing.property_mappings must be an object keyed by property path'];
480
+ }
481
+
482
+ const entries = Object.entries(value as Record<string, unknown>);
483
+ const issues: string[] = [];
484
+ if (entries.length > MAX_PROJECT_SEARCH_PROPERTY_MAPPINGS) {
485
+ issues.push(`indexing.property_mappings must contain at most ${MAX_PROJECT_SEARCH_PROPERTY_MAPPINGS} fields`);
486
+ }
487
+
488
+ const supportedTypes = new Set<string>(PROJECT_SEARCH_PROPERTY_TYPES);
489
+ for (const [path, rawMapping] of entries) {
490
+ const field = `indexing.property_mappings.${path}`;
491
+ if (!ELASTICSEARCH_FIELD_PATH_PATTERN.test(path)) {
492
+ issues.push(`${field} must be a dot-separated path containing only letters, numbers, and underscores`);
493
+ }
494
+ if (!rawMapping || typeof rawMapping !== 'object' || Array.isArray(rawMapping)) {
495
+ issues.push(`${field} must be an object`);
496
+ continue;
497
+ }
498
+ const mapping = rawMapping as Record<string, unknown>;
499
+ const extraKeys = Object.keys(mapping).filter(
500
+ (key) => !['type', 'format', 'ignore_above', 'ignore_malformed'].includes(key),
501
+ );
502
+ if (extraKeys.length > 0) {
503
+ issues.push(`${field} contains unsupported option(s): ${extraKeys.join(', ')}`);
504
+ }
505
+ if (typeof mapping.type !== 'string' || !supportedTypes.has(mapping.type)) {
506
+ issues.push(`${field}.type must be one of: ${PROJECT_SEARCH_PROPERTY_TYPES.join(', ')}`);
507
+ }
508
+ if (mapping.format !== undefined && (mapping.type !== 'date' || typeof mapping.format !== 'string')) {
509
+ issues.push(`${field}.format is supported only for date mappings`);
510
+ }
511
+ if (
512
+ mapping.ignore_above !== undefined &&
513
+ (mapping.type !== 'keyword' ||
514
+ !Number.isInteger(mapping.ignore_above) ||
515
+ (mapping.ignore_above as number) < 1 ||
516
+ (mapping.ignore_above as number) > MAX_KEYWORD_IGNORE_ABOVE)
517
+ ) {
518
+ issues.push(
519
+ `${field}.ignore_above is supported only for keyword mappings and must be an integer from 1 to ${MAX_KEYWORD_IGNORE_ABOVE}`,
520
+ );
521
+ }
522
+ if (
523
+ mapping.ignore_malformed !== undefined &&
524
+ (!['long', 'double', 'date'].includes(String(mapping.type)) ||
525
+ typeof mapping.ignore_malformed !== 'boolean')
526
+ ) {
527
+ issues.push(`${field}.ignore_malformed is supported only for long, double, and date mappings`);
528
+ }
529
+ }
530
+ return issues;
531
+ }
532
+
533
+ export interface ProjectIndexingConfiguration {
534
+ /**
535
+ * Enable indexing for content objects in this project.
536
+ * When enabled, content changes trigger indexing workflows.
537
+ * Defaults to true - indexing is always on when ES infrastructure is available.
538
+ */
539
+ enabled?: boolean;
540
+
541
+ /**
542
+ * Search tier for this project.
543
+ * standard uses the regional hosted Elasticsearch deployment.
544
+ * performance uses the regional serverless Elasticsearch project.
545
+ * Defaults to standard when omitted.
546
+ */
547
+ search_tier?: ProjectSearchTier;
548
+
549
+ /**
550
+ * Elasticsearch backend override for this project.
551
+ * Prefer search_tier for project configuration unless an explicit backend override is needed.
552
+ */
553
+ backend?: ElasticsearchBackend;
554
+
555
+ /**
556
+ * Explicit mappings for selected content-object property paths.
557
+ *
558
+ * Keys are dot-separated paths relative to `properties`, for example
559
+ * `order_total` or `customer.account_number`. Unlisted fields are mapped
560
+ * dynamically from their JSON values. Changing this value requires a full
561
+ * reindex.
562
+ */
563
+ property_mappings?: Record<string, ProjectSearchPropertyMapping>;
564
+ }
565
+
385
566
  // export interface ProjectConfigurationEmbeddings {
386
567
  // environment: string;
387
568
  // max_tokens: number;
@@ -937,6 +1118,8 @@ export interface IndexConfiguration {
937
1118
  };
938
1119
  /** ISO 639-1 language code for text analysis */
939
1120
  language?: string;
1121
+ /** Explicit mappings for selected content-object property paths. */
1122
+ property_mappings?: Record<string, ProjectSearchPropertyMapping>;
940
1123
  field_mappings?: Record<string, unknown>;
941
1124
  project_embeddings_config?: {
942
1125
  text?: EmbeddingTypeConfig;
package/src/query.ts CHANGED
@@ -88,6 +88,12 @@ export interface RunSearchQuery extends SimpleSearchQuery {
88
88
  model?: string;
89
89
  status?: ExecutionRunStatus;
90
90
  tags?: string[];
91
+ /**
92
+ * Tags to exclude. Runs carrying any of these tags are filtered out of the results,
93
+ * counts, and facet buckets. Combined with `tags` (which requires all of the listed
94
+ * tags) as an additional `$nin` constraint on the same field.
95
+ */
96
+ exclude_tags?: string[];
91
97
  query?: string;
92
98
  default_query_path?: string;
93
99
  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;