@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.
- package/lib/apps.d.ts +184 -48
- package/lib/apps.d.ts.map +1 -1
- package/lib/apps.js +21 -9
- package/lib/apps.js.map +1 -1
- package/lib/audit-trail.d.ts +61 -1
- package/lib/audit-trail.d.ts.map +1 -1
- package/lib/audit-trail.js +15 -0
- package/lib/audit-trail.js.map +1 -1
- package/lib/environment.d.ts +2 -0
- package/lib/environment.d.ts.map +1 -1
- package/lib/environment.js.map +1 -1
- package/lib/index.d.ts +7 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +7 -0
- package/lib/index.js.map +1 -1
- package/lib/interaction.d.ts +77 -1
- package/lib/interaction.d.ts.map +1 -1
- package/lib/interaction.js +48 -0
- package/lib/interaction.js.map +1 -1
- package/lib/platform-event.d.ts +37 -3
- package/lib/platform-event.d.ts.map +1 -1
- package/lib/platform-event.js.map +1 -1
- package/lib/project.d.ts +119 -20
- package/lib/project.d.ts.map +1 -1
- package/lib/project.js +65 -0
- package/lib/project.js.map +1 -1
- package/lib/query.d.ts +6 -0
- package/lib/query.d.ts.map +1 -1
- package/lib/refs.d.ts +1 -0
- package/lib/refs.d.ts.map +1 -1
- package/lib/schema-for-extraction.d.ts +21 -0
- package/lib/schema-for-extraction.d.ts.map +1 -0
- package/lib/schema-for-extraction.js +205 -0
- package/lib/schema-for-extraction.js.map +1 -0
- package/lib/store/agent-run.d.ts +23 -1
- package/lib/store/agent-run.d.ts.map +1 -1
- package/lib/store/conversation-state.d.ts +3 -1
- package/lib/store/conversation-state.d.ts.map +1 -1
- package/lib/store/conversation-state.js.map +1 -1
- package/lib/store/doc-analyzer.d.ts +10 -67
- package/lib/store/doc-analyzer.d.ts.map +1 -1
- package/lib/store/dsl-workflow.d.ts +1 -0
- package/lib/store/dsl-workflow.d.ts.map +1 -1
- package/lib/store/dsl-workflow.js.map +1 -1
- package/lib/store/grounded-extraction.d.ts +146 -0
- package/lib/store/grounded-extraction.d.ts.map +1 -0
- package/lib/store/grounded-extraction.js +8 -0
- package/lib/store/grounded-extraction.js.map +1 -0
- package/lib/store/index.d.ts +1 -0
- package/lib/store/index.d.ts.map +1 -1
- package/lib/store/index.js +1 -0
- package/lib/store/index.js.map +1 -1
- package/lib/store/store.d.ts +314 -1
- package/lib/store/store.d.ts.map +1 -1
- package/lib/store/store.js +451 -0
- package/lib/store/store.js.map +1 -1
- package/lib/store/workflow.d.ts +8 -1
- package/lib/store/workflow.d.ts.map +1 -1
- package/lib/store/workflow.js +5 -0
- package/lib/store/workflow.js.map +1 -1
- package/lib/user.d.ts +14 -0
- package/lib/user.d.ts.map +1 -1
- package/lib/user.js +31 -0
- package/lib/user.js.map +1 -1
- package/lib/vertesia-common.js +2 -2
- package/lib/vertesia-common.js.map +1 -1
- package/lib/view-configuration-validation.d.ts +15 -0
- package/lib/view-configuration-validation.d.ts.map +1 -0
- package/lib/view-configuration-validation.js +63 -0
- package/lib/view-configuration-validation.js.map +1 -0
- package/lib/view-query-validation.d.ts +13 -0
- package/lib/view-query-validation.d.ts.map +1 -0
- package/lib/view-query-validation.js +266 -0
- package/lib/view-query-validation.js.map +1 -0
- package/lib/view-validation-helpers.d.ts +15 -0
- package/lib/view-validation-helpers.d.ts.map +1 -0
- package/lib/view-validation-helpers.js +25 -0
- package/lib/view-validation-helpers.js.map +1 -0
- package/lib/views-schema.d.ts +1992 -0
- package/lib/views-schema.d.ts.map +1 -0
- package/lib/views-schema.js +674 -0
- package/lib/views-schema.js.map +1 -0
- package/lib/views-validation.d.ts +21 -0
- package/lib/views-validation.d.ts.map +1 -0
- package/lib/views-validation.js +164 -0
- package/lib/views-validation.js.map +1 -0
- package/lib/views.d.ts +381 -0
- package/lib/views.d.ts.map +1 -0
- package/lib/views.js +41 -0
- package/lib/views.js.map +1 -0
- package/package.json +5 -5
- package/src/agent-resources.test.ts +100 -0
- package/src/apps.test.ts +9 -1
- package/src/apps.ts +220 -73
- package/src/audit-trail.ts +83 -0
- package/src/environment.ts +2 -0
- package/src/index.ts +12 -0
- package/src/interaction.ts +135 -1
- package/src/platform-event.ts +39 -2
- package/src/project.test.ts +44 -0
- package/src/project.ts +205 -22
- package/src/query.ts +6 -0
- package/src/refs.ts +1 -0
- package/src/schema-for-extraction.test.ts +191 -0
- package/src/schema-for-extraction.ts +231 -0
- package/src/store/agent-run.ts +29 -0
- package/src/store/content-type-editing.test.ts +17 -0
- package/src/store/conversation-state.ts +4 -1
- package/src/store/doc-analyzer.ts +10 -76
- package/src/store/dsl-workflow.ts +1 -0
- package/src/store/grounded-extraction.ts +154 -0
- package/src/store/index.ts +1 -0
- package/src/store/store.ts +800 -1
- package/src/store/workflow.ts +12 -0
- package/src/user.ts +46 -0
- package/src/view-configuration-validation.ts +74 -0
- package/src/view-query-validation.test.ts +21 -0
- package/src/view-query-validation.ts +319 -0
- package/src/view-validation-helpers.ts +28 -0
- package/src/views-schema.test.ts +364 -0
- package/src/views-schema.ts +689 -0
- package/src/views-validation.ts +234 -0
- package/src/views.ts +484 -0
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import {
|
|
3
|
+
isExtractableSchemaNode,
|
|
4
|
+
mergePreservingNonExtractable,
|
|
5
|
+
schemaForExtraction,
|
|
6
|
+
} from './schema-for-extraction.js';
|
|
7
|
+
|
|
8
|
+
describe('schemaForExtraction', () => {
|
|
9
|
+
it('drops top-level properties marked x-extract: false and cleans required', () => {
|
|
10
|
+
const schema = {
|
|
11
|
+
type: 'object',
|
|
12
|
+
properties: {
|
|
13
|
+
po_number: { type: 'string' },
|
|
14
|
+
match: { type: 'string', 'x-extract': false },
|
|
15
|
+
match_reason: { type: 'string', 'x-extract': false },
|
|
16
|
+
},
|
|
17
|
+
required: ['po_number', 'match'],
|
|
18
|
+
};
|
|
19
|
+
const filtered = schemaForExtraction(schema);
|
|
20
|
+
expect(Object.keys(filtered.properties as object).sort()).toEqual(['po_number']);
|
|
21
|
+
expect(filtered.required).toEqual(['po_number']);
|
|
22
|
+
expect((filtered.properties as Record<string, unknown>).po_number).toEqual({ type: 'string' });
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('drops nested and array-item properties with x-extract: false', () => {
|
|
26
|
+
const schema = {
|
|
27
|
+
type: 'object',
|
|
28
|
+
properties: {
|
|
29
|
+
line_items: {
|
|
30
|
+
type: 'array',
|
|
31
|
+
items: {
|
|
32
|
+
type: 'object',
|
|
33
|
+
properties: {
|
|
34
|
+
part_number: { type: 'string' },
|
|
35
|
+
match: { type: 'string', 'x-extract': false },
|
|
36
|
+
quantity: { type: 'string' },
|
|
37
|
+
},
|
|
38
|
+
required: ['part_number', 'match'],
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
const filtered = schemaForExtraction(schema);
|
|
44
|
+
const items = (filtered.properties as Record<string, Record<string, unknown>>).line_items.items as Record<
|
|
45
|
+
string,
|
|
46
|
+
unknown
|
|
47
|
+
>;
|
|
48
|
+
expect(Object.keys(items.properties as object).sort()).toEqual(['part_number', 'quantity']);
|
|
49
|
+
expect(items.required).toEqual(['part_number']);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('treats missing x-extract as extractable', () => {
|
|
53
|
+
expect(isExtractableSchemaNode({ type: 'string' })).toBe(true);
|
|
54
|
+
expect(isExtractableSchemaNode({ type: 'string', 'x-extract': true })).toBe(true);
|
|
55
|
+
expect(isExtractableSchemaNode({ type: 'string', 'x-extract': false })).toBe(false);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('filters $defs and drops properties that reference non-extractable definitions', () => {
|
|
59
|
+
const schema = {
|
|
60
|
+
type: 'object',
|
|
61
|
+
$defs: {
|
|
62
|
+
hidden: { type: 'string', 'x-extract': false },
|
|
63
|
+
visible: {
|
|
64
|
+
type: 'object',
|
|
65
|
+
properties: {
|
|
66
|
+
value: { type: 'string' },
|
|
67
|
+
match: { type: 'string', 'x-extract': false },
|
|
68
|
+
},
|
|
69
|
+
required: ['value', 'match'],
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
properties: {
|
|
73
|
+
hidden: { $ref: '#/$defs/hidden' },
|
|
74
|
+
visible: { $ref: '#/$defs/visible' },
|
|
75
|
+
},
|
|
76
|
+
required: ['hidden', 'visible'],
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const filtered = schemaForExtraction(schema);
|
|
80
|
+
|
|
81
|
+
expect(filtered.$defs.hidden).toBeUndefined();
|
|
82
|
+
expect(filtered.$defs.visible).toEqual({
|
|
83
|
+
type: 'object',
|
|
84
|
+
properties: { value: { type: 'string' } },
|
|
85
|
+
required: ['value'],
|
|
86
|
+
});
|
|
87
|
+
expect(filtered.properties).toEqual({ visible: { $ref: '#/$defs/visible' } });
|
|
88
|
+
expect(filtered.required).toEqual(['visible']);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('drops non-extractable anyOf branches', () => {
|
|
92
|
+
const schema = {
|
|
93
|
+
type: 'object',
|
|
94
|
+
properties: {
|
|
95
|
+
value: {
|
|
96
|
+
anyOf: [{ type: 'string', 'x-extract': false }, { type: 'number' }],
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
const filtered = schemaForExtraction(schema);
|
|
102
|
+
|
|
103
|
+
expect((filtered.properties.value as { anyOf: unknown[] }).anyOf).toEqual([{ type: 'number' }]);
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
describe('mergePreservingNonExtractable', () => {
|
|
108
|
+
it('preserves non-extractable top-level values over model output', () => {
|
|
109
|
+
const schema = {
|
|
110
|
+
type: 'object',
|
|
111
|
+
properties: {
|
|
112
|
+
po_number: { type: 'string' },
|
|
113
|
+
match: { type: 'string', 'x-extract': false },
|
|
114
|
+
},
|
|
115
|
+
};
|
|
116
|
+
const existing = { po_number: 'OLD', match: 'erp-hit' };
|
|
117
|
+
const extracted = { po_number: 'NEW', match: 'model-invented' };
|
|
118
|
+
expect(mergePreservingNonExtractable(existing, extracted, schema)).toEqual({
|
|
119
|
+
po_number: 'NEW',
|
|
120
|
+
match: 'erp-hit',
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it('clears extractable fields omitted by the latest extraction', () => {
|
|
125
|
+
const schema = {
|
|
126
|
+
type: 'object',
|
|
127
|
+
properties: {
|
|
128
|
+
po_number: { type: 'string' },
|
|
129
|
+
total: { type: 'number' },
|
|
130
|
+
match: { type: 'string', 'x-extract': false },
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
const existing = { po_number: 'STALE', total: 42, match: 'erp-hit' };
|
|
134
|
+
const extracted = { total: 50 };
|
|
135
|
+
|
|
136
|
+
expect(mergePreservingNonExtractable(existing, extracted, schema)).toEqual({
|
|
137
|
+
total: 50,
|
|
138
|
+
match: 'erp-hit',
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it('does not preserve non-extractable array item fields by index', () => {
|
|
143
|
+
const schema = {
|
|
144
|
+
type: 'object',
|
|
145
|
+
properties: {
|
|
146
|
+
line_items: {
|
|
147
|
+
type: 'array',
|
|
148
|
+
items: {
|
|
149
|
+
type: 'object',
|
|
150
|
+
properties: {
|
|
151
|
+
part: { type: 'string' },
|
|
152
|
+
match: { type: 'string', 'x-extract': false },
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
},
|
|
157
|
+
};
|
|
158
|
+
const existing = {
|
|
159
|
+
line_items: [
|
|
160
|
+
{ part: 'A', match: 'hit-a' },
|
|
161
|
+
{ part: 'B', match: 'hit-b' },
|
|
162
|
+
],
|
|
163
|
+
};
|
|
164
|
+
const extracted = {
|
|
165
|
+
line_items: [{ part: 'B2' }],
|
|
166
|
+
};
|
|
167
|
+
expect(mergePreservingNonExtractable(existing, extracted, schema)).toEqual({
|
|
168
|
+
line_items: [{ part: 'B2' }],
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
it('preserves top-level values referenced through non-extractable $defs', () => {
|
|
173
|
+
const schema = {
|
|
174
|
+
type: 'object',
|
|
175
|
+
$defs: {
|
|
176
|
+
match: { type: 'string', 'x-extract': false },
|
|
177
|
+
},
|
|
178
|
+
properties: {
|
|
179
|
+
po_number: { type: 'string' },
|
|
180
|
+
match: { $ref: '#/$defs/match' },
|
|
181
|
+
},
|
|
182
|
+
};
|
|
183
|
+
const existing = { po_number: 'OLD', match: 'erp-hit' };
|
|
184
|
+
const extracted = { po_number: 'NEW', match: 'model-invented' };
|
|
185
|
+
|
|
186
|
+
expect(mergePreservingNonExtractable(existing, extracted, schema)).toEqual({
|
|
187
|
+
po_number: 'NEW',
|
|
188
|
+
match: 'erp-hit',
|
|
189
|
+
});
|
|
190
|
+
});
|
|
191
|
+
});
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extraction schema filtering.
|
|
3
|
+
*
|
|
4
|
+
* Content-type object_schema properties may set `"x-extract": false` to mark fields
|
|
5
|
+
* that must not be filled by extraction models (match scores, ERP ids filled later,
|
|
6
|
+
* internal bookkeeping). The full schema remains the object model for UI/storage;
|
|
7
|
+
* only the filtered copy is used as result_schema for constrained decoding.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export const X_EXTRACT = 'x-extract';
|
|
11
|
+
|
|
12
|
+
function unescapeJsonPointerSegment(segment: string): string {
|
|
13
|
+
return segment.replace(/~1/g, '/').replace(/~0/g, '~');
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function resolveLocalRef(ref: string, root: unknown): unknown {
|
|
17
|
+
if (!ref.startsWith('#/')) {
|
|
18
|
+
return undefined;
|
|
19
|
+
}
|
|
20
|
+
let current = root;
|
|
21
|
+
for (const segment of ref.slice(2).split('/').map(unescapeJsonPointerSegment)) {
|
|
22
|
+
if (!current || typeof current !== 'object' || Array.isArray(current)) {
|
|
23
|
+
return undefined;
|
|
24
|
+
}
|
|
25
|
+
current = (current as Record<string, unknown>)[segment];
|
|
26
|
+
}
|
|
27
|
+
return current;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function isExtractableSchemaNodeInternal(schema: unknown, root: unknown, seenRefs: Set<string>): boolean {
|
|
31
|
+
if (!schema || typeof schema !== 'object' || Array.isArray(schema)) {
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
const schemaObj = schema as Record<string, unknown>;
|
|
35
|
+
if (schemaObj[X_EXTRACT] === false) {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
if (typeof schemaObj.$ref === 'string' && !seenRefs.has(schemaObj.$ref)) {
|
|
39
|
+
seenRefs.add(schemaObj.$ref);
|
|
40
|
+
const resolved = resolveLocalRef(schemaObj.$ref, root);
|
|
41
|
+
if (resolved !== undefined) {
|
|
42
|
+
return isExtractableSchemaNodeInternal(resolved, root, seenRefs);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function isExtractableSchemaNode(schema: unknown, root: unknown = schema): boolean {
|
|
49
|
+
return isExtractableSchemaNodeInternal(schema, root, new Set());
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function filterSchemaArray(value: unknown, root: unknown): unknown[] | undefined {
|
|
53
|
+
if (!Array.isArray(value)) {
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
56
|
+
const filtered = value
|
|
57
|
+
.map((item) => schemaForExtractionNode(item, root))
|
|
58
|
+
.filter((item): item is unknown => item !== undefined);
|
|
59
|
+
return filtered.length > 0 ? filtered : undefined;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function filterSchemaMap(value: unknown, root: unknown): Record<string, unknown> | undefined {
|
|
63
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
64
|
+
return undefined;
|
|
65
|
+
}
|
|
66
|
+
const filtered: Record<string, unknown> = {};
|
|
67
|
+
for (const [key, child] of Object.entries(value)) {
|
|
68
|
+
const childFiltered = schemaForExtractionNode(child, root);
|
|
69
|
+
if (childFiltered !== undefined) {
|
|
70
|
+
filtered[key] = childFiltered;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return Object.keys(filtered).length > 0 ? filtered : undefined;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function schemaForExtractionNode(schema: unknown, root: unknown): unknown {
|
|
77
|
+
if (!schema || typeof schema !== 'object') {
|
|
78
|
+
return schema;
|
|
79
|
+
}
|
|
80
|
+
if (Array.isArray(schema)) {
|
|
81
|
+
return schema.map((item) => schemaForExtractionNode(item, root));
|
|
82
|
+
}
|
|
83
|
+
if (!isExtractableSchemaNode(schema, root)) {
|
|
84
|
+
return undefined;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const node = { ...(schema as Record<string, unknown>) };
|
|
88
|
+
|
|
89
|
+
for (const defsKey of ['$defs', 'definitions']) {
|
|
90
|
+
const defs = filterSchemaMap(node[defsKey], root);
|
|
91
|
+
if (defs) {
|
|
92
|
+
node[defsKey] = defs;
|
|
93
|
+
} else {
|
|
94
|
+
delete node[defsKey];
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (node.items && typeof node.items === 'object') {
|
|
99
|
+
const items = schemaForExtractionNode(node.items, root);
|
|
100
|
+
if (items !== undefined) {
|
|
101
|
+
node.items = items;
|
|
102
|
+
} else {
|
|
103
|
+
delete node.items;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
for (const unionKey of ['anyOf', 'oneOf', 'allOf']) {
|
|
107
|
+
if (Array.isArray(node[unionKey])) {
|
|
108
|
+
const filtered = filterSchemaArray(node[unionKey], root);
|
|
109
|
+
if (!filtered) {
|
|
110
|
+
return undefined;
|
|
111
|
+
}
|
|
112
|
+
node[unionKey] = filtered;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
if (node.additionalProperties && typeof node.additionalProperties === 'object') {
|
|
116
|
+
const additionalProperties = schemaForExtractionNode(node.additionalProperties, root);
|
|
117
|
+
node.additionalProperties = additionalProperties ?? false;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (node.properties && typeof node.properties === 'object' && !Array.isArray(node.properties)) {
|
|
121
|
+
const props: Record<string, unknown> = {};
|
|
122
|
+
for (const [key, child] of Object.entries(node.properties as Record<string, unknown>)) {
|
|
123
|
+
const childFiltered = schemaForExtractionNode(child, root);
|
|
124
|
+
if (childFiltered !== undefined) {
|
|
125
|
+
props[key] = childFiltered;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
node.properties = props;
|
|
129
|
+
|
|
130
|
+
if (Array.isArray(node.required)) {
|
|
131
|
+
const kept = node.required.filter((r) => typeof r === 'string' && r in props);
|
|
132
|
+
if (kept.length > 0) {
|
|
133
|
+
node.required = kept;
|
|
134
|
+
} else {
|
|
135
|
+
delete node.required;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Drop the flag from the extraction copy (not needed by providers)
|
|
141
|
+
delete node[X_EXTRACT];
|
|
142
|
+
|
|
143
|
+
return node;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Deep-clones a JSON schema and removes properties (recursively) marked
|
|
148
|
+
* `x-extract: false`, cleaning `required` arrays to match.
|
|
149
|
+
*/
|
|
150
|
+
export function schemaForExtraction<T>(schema: T): T {
|
|
151
|
+
return (schemaForExtractionNode(schema, schema) ?? {}) as T;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function resolveSchemaNode(schema: unknown, root: unknown): unknown {
|
|
155
|
+
if (!schema || typeof schema !== 'object' || Array.isArray(schema)) {
|
|
156
|
+
return schema;
|
|
157
|
+
}
|
|
158
|
+
const schemaObj = schema as Record<string, unknown>;
|
|
159
|
+
if (typeof schemaObj.$ref !== 'string') {
|
|
160
|
+
return schema;
|
|
161
|
+
}
|
|
162
|
+
return resolveLocalRef(schemaObj.$ref, root) ?? schema;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function mergePreservingNonExtractableNode(
|
|
166
|
+
existing: unknown,
|
|
167
|
+
extracted: unknown,
|
|
168
|
+
schema: unknown,
|
|
169
|
+
root: unknown,
|
|
170
|
+
): unknown {
|
|
171
|
+
if (!schema || typeof schema !== 'object' || Array.isArray(schema)) {
|
|
172
|
+
return extracted;
|
|
173
|
+
}
|
|
174
|
+
const resolved = resolveSchemaNode(schema, root);
|
|
175
|
+
if (!resolved || typeof resolved !== 'object' || Array.isArray(resolved)) {
|
|
176
|
+
return extracted;
|
|
177
|
+
}
|
|
178
|
+
const schemaObj = resolved as Record<string, unknown>;
|
|
179
|
+
|
|
180
|
+
// Arrays are new extraction output. Without a declared stable item key, index
|
|
181
|
+
// merging can attach preserved ERP/match fields to the wrong row.
|
|
182
|
+
if (Array.isArray(extracted)) {
|
|
183
|
+
const itemSchema = schemaObj.items;
|
|
184
|
+
if (!itemSchema || typeof itemSchema !== 'object') {
|
|
185
|
+
return extracted;
|
|
186
|
+
}
|
|
187
|
+
return extracted.map((item) => mergePreservingNonExtractableNode(undefined, item, itemSchema, root));
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (!extracted || typeof extracted !== 'object' || Array.isArray(extracted)) {
|
|
191
|
+
return extracted;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const props = schemaObj.properties;
|
|
195
|
+
if (!props || typeof props !== 'object' || Array.isArray(props)) {
|
|
196
|
+
return extracted;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const existingObj =
|
|
200
|
+
existing && typeof existing === 'object' && !Array.isArray(existing)
|
|
201
|
+
? (existing as Record<string, unknown>)
|
|
202
|
+
: {};
|
|
203
|
+
const extractedObj = extracted as Record<string, unknown>;
|
|
204
|
+
const propSchemas = props as Record<string, unknown>;
|
|
205
|
+
const result: Record<string, unknown> = { ...extractedObj };
|
|
206
|
+
|
|
207
|
+
for (const [key, propSchema] of Object.entries(propSchemas)) {
|
|
208
|
+
if (!isExtractableSchemaNode(propSchema, root)) {
|
|
209
|
+
// Model should not have filled this; restore prior value if any.
|
|
210
|
+
if (key in existingObj) {
|
|
211
|
+
result[key] = existingObj[key];
|
|
212
|
+
} else {
|
|
213
|
+
delete result[key];
|
|
214
|
+
}
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
217
|
+
if (key in extractedObj) {
|
|
218
|
+
result[key] = mergePreservingNonExtractableNode(existingObj[key], extractedObj[key], propSchema, root);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
return result;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Merge model output over existing properties while preserving values at paths
|
|
227
|
+
* marked `x-extract: false` on the full (unfiltered) schema.
|
|
228
|
+
*/
|
|
229
|
+
export function mergePreservingNonExtractable(existing: unknown, extracted: unknown, schema: unknown): unknown {
|
|
230
|
+
return mergePreservingNonExtractableNode(existing, extracted, schema, schema);
|
|
231
|
+
}
|
package/src/store/agent-run.ts
CHANGED
|
@@ -16,6 +16,7 @@ import type { UserChannel } from '../email.js';
|
|
|
16
16
|
import type {
|
|
17
17
|
AgentSearchScope,
|
|
18
18
|
ConversationVisibility,
|
|
19
|
+
InitialToolCall,
|
|
19
20
|
InteractionExecutionConfiguration,
|
|
20
21
|
InteractionRef,
|
|
21
22
|
RunSource,
|
|
@@ -173,6 +174,15 @@ export interface AgentRunBase<TData = Record<string, unknown>, TProperties = Rec
|
|
|
173
174
|
/** Tools configured for this run (+/- syntax supported) */
|
|
174
175
|
tool_names?: string[];
|
|
175
176
|
|
|
177
|
+
/** Builtin system skills activated before the first model turn. */
|
|
178
|
+
initial_skills?: string[];
|
|
179
|
+
|
|
180
|
+
/** Ordered, bounded hydration/read calls executed before the first model turn. */
|
|
181
|
+
initial_tool_calls?: InitialToolCall[];
|
|
182
|
+
|
|
183
|
+
/** Hard denylist of tool names: never exposed to the model, refused at execution time. */
|
|
184
|
+
excluded_tools?: string[];
|
|
185
|
+
|
|
176
186
|
/** Scoped collection (if any) */
|
|
177
187
|
collection_id?: string;
|
|
178
188
|
|
|
@@ -547,6 +557,25 @@ export interface AgentArtifactUrlResponse {
|
|
|
547
557
|
path: string;
|
|
548
558
|
}
|
|
549
559
|
|
|
560
|
+
/** Text content and concurrency token for an agent artifact. */
|
|
561
|
+
export interface AgentArtifactContentResponse {
|
|
562
|
+
path: string;
|
|
563
|
+
content: string;
|
|
564
|
+
generation: string;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
/** Conditional text update for an agent artifact. */
|
|
568
|
+
export interface UpdateAgentArtifactContentPayload {
|
|
569
|
+
content: string;
|
|
570
|
+
generation: string;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
/** Result of a conditional agent artifact update. */
|
|
574
|
+
export interface UpdateAgentArtifactContentResponse {
|
|
575
|
+
path: string;
|
|
576
|
+
generation: string;
|
|
577
|
+
}
|
|
578
|
+
|
|
550
579
|
/**
|
|
551
580
|
* Telemetry ingestion payload for an agent run.
|
|
552
581
|
*/
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import Ajv from 'ajv';
|
|
2
|
+
import { describe, expect, it } from 'vitest';
|
|
3
|
+
import { ContentTypeEditingPolicySchema } from './store.js';
|
|
4
|
+
|
|
5
|
+
const validate = new Ajv.default({ allErrors: true, strict: false }).compile(ContentTypeEditingPolicySchema);
|
|
6
|
+
|
|
7
|
+
describe('ContentTypeEditingPolicySchema', () => {
|
|
8
|
+
it('accepts an omitted or configured interaction', () => {
|
|
9
|
+
expect(validate({})).toBe(true);
|
|
10
|
+
expect(validate({ interaction: 'sys:GeneralAgent' })).toBe(true);
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it('rejects unknown fields and non-string interactions', () => {
|
|
14
|
+
expect(validate({ interaction: 42 })).toBe(false);
|
|
15
|
+
expect(validate({ interaction: 'sys:GeneralAgent', tools: [] })).toBe(false);
|
|
16
|
+
});
|
|
17
|
+
});
|
|
@@ -206,6 +206,9 @@ export interface ConversationState {
|
|
|
206
206
|
*/
|
|
207
207
|
skill_instructions_delivered?: string[];
|
|
208
208
|
|
|
209
|
+
/** Stable ids of initialization tool calls completed before the first model turn. */
|
|
210
|
+
initialization_call_ids?: string[];
|
|
211
|
+
|
|
209
212
|
/**
|
|
210
213
|
* Denylist of MCP tool-collection ids deactivated for this conversation.
|
|
211
214
|
* `undefined`/empty means all installed/connected MCP collections are active.
|
|
@@ -243,7 +246,7 @@ export interface ConversationState {
|
|
|
243
246
|
launch_id?: string;
|
|
244
247
|
|
|
245
248
|
/**
|
|
246
|
-
* The app version this run is pinned to
|
|
249
|
+
* The exact app version this run is pinned to, derived from the `@version` on the
|
|
247
250
|
* started interaction ref / the `x-vertesia-app-version` header at start. Persisted on the state
|
|
248
251
|
* so it survives resume, and applied to the activity client (`withAppVersion`) so every app-owned
|
|
249
252
|
* ref the run resolves — interactions, types, processes, tools — targets this version instead of
|
|
@@ -1,104 +1,42 @@
|
|
|
1
|
-
import type { JSONObject } from '../json.js';
|
|
2
1
|
import type { WorkflowExecutionPayload, WorkflowRunStatus } from './workflow.js';
|
|
3
2
|
|
|
4
|
-
export interface
|
|
3
|
+
export interface DocumentPrepOptions {
|
|
5
4
|
features?: string[];
|
|
6
5
|
debug?: boolean;
|
|
6
|
+
output_format?: DocProcessorOutputFormat;
|
|
7
7
|
[key: string]: unknown;
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
export interface
|
|
11
|
-
vars:
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export interface TransformTablesWorkflowPayload extends Omit<WorkflowExecutionPayload, 'vars'> {
|
|
15
|
-
vars: AdaptTablesParams;
|
|
16
|
-
environment?: string;
|
|
10
|
+
export interface DocumentPrepWorkflowPayload extends Omit<WorkflowExecutionPayload, 'vars'> {
|
|
11
|
+
vars: DocumentPrepOptions;
|
|
17
12
|
}
|
|
18
13
|
|
|
19
|
-
export
|
|
20
|
-
result_path: string;
|
|
21
|
-
status: string;
|
|
22
|
-
table_count: number;
|
|
23
|
-
item_count: number;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Represents a image in a document that has been analyzed
|
|
28
|
-
*/
|
|
29
|
-
export interface DocImage {
|
|
30
|
-
id?: string;
|
|
31
|
-
page_number?: number;
|
|
32
|
-
description?: string;
|
|
33
|
-
is_meaningful?: boolean;
|
|
34
|
-
width?: number;
|
|
35
|
-
height?: number;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* The export type formats for tables.
|
|
40
|
-
*/
|
|
41
|
-
export type ExportTableFormats = 'json' | 'csv' | 'xml';
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Represents a table in a document that has been analyzed
|
|
45
|
-
*/
|
|
46
|
-
export interface DocTable {
|
|
47
|
-
page_number?: number;
|
|
48
|
-
table_number?: number;
|
|
49
|
-
title?: string;
|
|
50
|
-
format: 'application/csv' | 'application/json';
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Represents a table in a document that has been analyzed in CSV format
|
|
55
|
-
*/
|
|
56
|
-
export interface DocTableCsv extends DocTable {
|
|
57
|
-
format: 'application/csv';
|
|
58
|
-
title?: string;
|
|
59
|
-
data: string;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Represents a table in a document that has been analyzed in JSON format
|
|
64
|
-
*/
|
|
65
|
-
export interface DocTableJson extends DocTable {
|
|
66
|
-
format: 'application/json';
|
|
67
|
-
title?: string;
|
|
68
|
-
data: JSONObject[];
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
export type DocTableResponse = DocTableCsv | DocTableJson;
|
|
14
|
+
export type DocumentProcessingPhase = 'markdown' | 'grounded_extraction';
|
|
72
15
|
|
|
73
16
|
/**
|
|
74
17
|
* Output format for document processing workflows
|
|
75
18
|
*/
|
|
76
|
-
export type DocProcessorOutputFormat = '
|
|
19
|
+
export type DocProcessorOutputFormat = 'markdown';
|
|
77
20
|
|
|
78
21
|
/**
|
|
79
22
|
* Represents a document analysis run status
|
|
80
23
|
*/
|
|
81
24
|
export interface DocAnalyzeRunStatusResponse extends WorkflowRunStatus {
|
|
25
|
+
phase?: DocumentProcessingPhase;
|
|
82
26
|
progress?: DocAnalyzerProgress;
|
|
83
|
-
/** The output format being used for processing
|
|
27
|
+
/** The output format being used for processing. */
|
|
84
28
|
output_format?: DocProcessorOutputFormat;
|
|
85
29
|
}
|
|
86
30
|
|
|
87
|
-
export interface DocAnalyzerResultResponse {
|
|
88
|
-
document?: string;
|
|
89
|
-
tables?: DocTableResponse[];
|
|
90
|
-
images?: DocImage[];
|
|
91
|
-
annotated?: string | null;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
31
|
export interface DocAnalyzerProgress {
|
|
32
|
+
phase?: DocumentProcessingPhase;
|
|
95
33
|
pages: DocAnalyzerProgressStatus;
|
|
96
34
|
images: DocAnalyzerProgressStatus;
|
|
97
35
|
tables: DocAnalyzerProgressStatus;
|
|
98
36
|
visuals: DocAnalyzerProgressStatus;
|
|
99
37
|
started_at?: number;
|
|
100
38
|
percent: number;
|
|
101
|
-
/** The output format being used for processing
|
|
39
|
+
/** The output format being used for processing. */
|
|
102
40
|
output_format?: DocProcessorOutputFormat;
|
|
103
41
|
}
|
|
104
42
|
|
|
@@ -176,7 +114,3 @@ export interface AdaptedTable {
|
|
|
176
114
|
}
|
|
177
115
|
|
|
178
116
|
export type AdaptedTableResponse = Record<string, AdaptedTable>;
|
|
179
|
-
|
|
180
|
-
export interface AnnotatedPdfResponse {
|
|
181
|
-
url: string | null;
|
|
182
|
-
}
|
|
@@ -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;
|