@vertesia/common 1.5.0 → 1.6.0-dev.20260918.061604Z
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/access-control-values.d.ts +13 -0
- package/lib/access-control-values.d.ts.map +1 -1
- package/lib/access-control-values.js +24 -0
- package/lib/access-control-values.js.map +1 -1
- package/lib/api-contract/components.generated.json +4446 -2194
- package/lib/api-schemas/account.d.ts +3 -10
- package/lib/api-schemas/account.d.ts.map +1 -1
- package/lib/api-schemas/account.js +5 -9
- package/lib/api-schemas/account.js.map +1 -1
- package/lib/api-schemas/adapter.d.ts.map +1 -1
- package/lib/api-schemas/adapter.js +8 -1
- package/lib/api-schemas/adapter.js.map +1 -1
- package/lib/api-schemas/agent-communication.d.ts +1 -0
- package/lib/api-schemas/agent-communication.d.ts.map +1 -1
- package/lib/api-schemas/agent-communication.js +6 -0
- package/lib/api-schemas/agent-communication.js.map +1 -1
- package/lib/api-schemas/agent-runs.d.ts +5286 -903
- package/lib/api-schemas/agent-runs.d.ts.map +1 -1
- package/lib/api-schemas/agent-runs.js +366 -1
- package/lib/api-schemas/agent-runs.js.map +1 -1
- package/lib/api-schemas/apikey.d.ts +55 -0
- package/lib/api-schemas/apikey.d.ts.map +1 -1
- package/lib/api-schemas/apikey.js +30 -0
- package/lib/api-schemas/apikey.js.map +1 -1
- package/lib/api-schemas/app-runtime.d.ts +1240 -22
- package/lib/api-schemas/app-runtime.d.ts.map +1 -1
- package/lib/api-schemas/content.d.ts +435 -0
- package/lib/api-schemas/content.d.ts.map +1 -1
- package/lib/api-schemas/delegation.d.ts +35 -0
- package/lib/api-schemas/delegation.d.ts.map +1 -0
- package/lib/api-schemas/delegation.js +21 -0
- package/lib/api-schemas/delegation.js.map +1 -0
- package/lib/api-schemas/document-processing.d.ts +261 -0
- package/lib/api-schemas/document-processing.d.ts.map +1 -1
- package/lib/api-schemas/emit-json-schema.d.ts +5 -0
- package/lib/api-schemas/emit-json-schema.d.ts.map +1 -0
- package/lib/api-schemas/emit-json-schema.js +37 -0
- package/lib/api-schemas/emit-json-schema.js.map +1 -0
- package/lib/api-schemas/environment.d.ts +18 -0
- package/lib/api-schemas/environment.d.ts.map +1 -1
- package/lib/api-schemas/events.d.ts +783 -0
- package/lib/api-schemas/events.d.ts.map +1 -1
- package/lib/api-schemas/index.d.ts +1 -0
- package/lib/api-schemas/index.d.ts.map +1 -1
- package/lib/api-schemas/index.js +1 -0
- package/lib/api-schemas/index.js.map +1 -1
- package/lib/api-schemas/interaction.d.ts +3939 -91
- package/lib/api-schemas/interaction.d.ts.map +1 -1
- package/lib/api-schemas/interaction.js +14 -3
- package/lib/api-schemas/interaction.js.map +1 -1
- package/lib/api-schemas/oauth-server.d.ts +1 -0
- package/lib/api-schemas/oauth-server.d.ts.map +1 -1
- package/lib/api-schemas/oauth-server.js +3 -0
- package/lib/api-schemas/oauth-server.js.map +1 -1
- package/lib/api-schemas/process-agent-policy.d.ts +69 -0
- package/lib/api-schemas/process-agent-policy.d.ts.map +1 -0
- package/lib/api-schemas/process-agent-policy.js +154 -0
- package/lib/api-schemas/process-agent-policy.js.map +1 -0
- package/lib/api-schemas/process.d.ts +1260 -0
- package/lib/api-schemas/process.d.ts.map +1 -1
- package/lib/api-schemas/process.js +29 -1
- package/lib/api-schemas/process.js.map +1 -1
- package/lib/api-schemas/project-configuration.d.ts +883 -8
- package/lib/api-schemas/project-configuration.d.ts.map +1 -1
- package/lib/api-schemas/project-configuration.js +3 -2
- package/lib/api-schemas/project-configuration.js.map +1 -1
- package/lib/api-schemas/project.d.ts +1330 -22
- package/lib/api-schemas/project.d.ts.map +1 -1
- package/lib/api-schemas/registry.d.ts +24347 -5694
- package/lib/api-schemas/registry.d.ts.map +1 -1
- package/lib/api-schemas/registry.js +50 -8
- package/lib/api-schemas/registry.js.map +1 -1
- package/lib/api-schemas/store.d.ts +4427 -77
- package/lib/api-schemas/store.d.ts.map +1 -1
- package/lib/api-schemas/sts.d.ts +6 -0
- package/lib/api-schemas/sts.d.ts.map +1 -1
- package/lib/api-schemas/sts.js +3 -0
- package/lib/api-schemas/sts.js.map +1 -1
- package/lib/api-schemas/workflow-runs.d.ts +189 -0
- package/lib/api-schemas/workflow-runs.d.ts.map +1 -1
- package/lib/api-schemas/workflow-runs.js +14 -0
- package/lib/api-schemas/workflow-runs.js.map +1 -1
- package/lib/apikey.d.ts +8 -0
- package/lib/apikey.d.ts.map +1 -1
- package/lib/apikey.js.map +1 -1
- package/lib/delegation.d.ts +13 -0
- package/lib/delegation.d.ts.map +1 -0
- package/lib/delegation.js +2 -0
- package/lib/delegation.js.map +1 -0
- package/lib/environment.d.ts +1 -0
- package/lib/environment.d.ts.map +1 -1
- package/lib/index.d.ts +1 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js.map +1 -1
- package/lib/oauth-scopes.d.ts.map +1 -1
- package/lib/oauth-scopes.js +3 -1
- package/lib/oauth-scopes.js.map +1 -1
- package/lib/project-values.d.ts +1 -0
- package/lib/project-values.d.ts.map +1 -1
- package/lib/project-values.js +1 -0
- package/lib/project-values.js.map +1 -1
- package/lib/store/agent-run-values.d.ts +25 -0
- package/lib/store/agent-run-values.d.ts.map +1 -0
- package/lib/store/agent-run-values.js +25 -0
- package/lib/store/agent-run-values.js.map +1 -0
- package/lib/store/agent-run.d.ts +22 -2
- package/lib/store/agent-run.d.ts.map +1 -1
- package/lib/store/agent-run.js +1 -1
- package/lib/store/agent-run.js.map +1 -1
- package/lib/store/conversation-state.d.ts +1 -1
- package/lib/store/conversation-state.d.ts.map +1 -1
- package/lib/store/intake-policy-schema.generated.d.ts.map +1 -1
- package/lib/store/intake-policy-schema.generated.js +124 -0
- package/lib/store/intake-policy-schema.generated.js.map +1 -1
- package/lib/store/process.d.ts +17 -0
- package/lib/store/process.d.ts.map +1 -1
- package/lib/store/process.js.map +1 -1
- package/lib/store/schedule.d.ts +62 -25
- package/lib/store/schedule.d.ts.map +1 -1
- package/lib/store/schedule.js +2 -2
- package/lib/store/schedule.js.map +1 -1
- package/lib/store/workflow.d.ts +2 -0
- package/lib/store/workflow.d.ts.map +1 -1
- package/lib/store/workflow.js.map +1 -1
- package/lib/vertesia-common.js +2 -2
- package/lib/vertesia-common.js.map +1 -1
- package/lib/workflow-analytics.d.ts +230 -2
- package/lib/workflow-analytics.d.ts.map +1 -1
- package/lib/workflow-analytics.js +8 -0
- package/lib/workflow-analytics.js.map +1 -1
- package/package.json +8 -8
- package/src/access-control-values.ts +26 -0
- package/src/api-contract/components.contract.test.ts +2 -2
- package/src/api-contract/components.generated.json +4446 -2194
- package/src/api-schemas/account-apikey.test.ts +60 -0
- package/src/api-schemas/account.contract.test.ts +2 -1
- package/src/api-schemas/account.ts +5 -9
- package/src/api-schemas/adapter-edge-cases.test.ts +19 -0
- package/src/api-schemas/adapter.ts +8 -1
- package/src/api-schemas/agent-communication.ts +6 -0
- package/src/api-schemas/agent-history.contract.test.ts +42 -0
- package/src/api-schemas/agent-runs.contract.test.ts +240 -0
- package/src/api-schemas/agent-runs.ts +399 -0
- package/src/api-schemas/apikey.ts +31 -0
- package/src/api-schemas/bundle-isolation.test.ts +5 -41
- package/src/api-schemas/delegation.ts +21 -0
- package/src/api-schemas/emit-json-schema.test.ts +36 -0
- package/src/api-schemas/emit-json-schema.ts +40 -0
- package/src/api-schemas/environment.contract.test.ts +3 -0
- package/src/api-schemas/index.ts +1 -0
- package/src/api-schemas/interaction.contract.test.ts +23 -0
- package/src/api-schemas/interaction.ts +15 -3
- package/src/api-schemas/llumiverse.contract.test.ts +3 -9
- package/src/api-schemas/oauth-server.ts +4 -0
- package/src/api-schemas/process-agent-policy.ts +167 -0
- package/src/api-schemas/process.contract.test.ts +115 -0
- package/src/api-schemas/process.ts +31 -1
- package/src/api-schemas/project-configuration.ts +3 -2
- package/src/api-schemas/project.contract.test.ts +15 -0
- package/src/api-schemas/registry.ts +59 -10
- package/src/api-schemas/store.contract.test.ts +4 -126
- package/src/api-schemas/sts.ts +3 -0
- package/src/api-schemas/workflow-runs.ts +16 -0
- package/src/api-schemas/zeno-response-contracts.test.ts +36 -0
- package/src/apikey.ts +13 -0
- package/src/delegation.ts +18 -0
- package/src/index.ts +6 -0
- package/src/oauth-scopes.ts +3 -1
- package/src/project-values.ts +1 -0
- package/src/roles.test.ts +1 -0
- package/src/store/agent-run-values.ts +27 -0
- package/src/store/agent-run.ts +36 -0
- package/src/store/conversation-state.ts +1 -1
- package/src/store/intake-policy-schema.generated.ts +125 -0
- package/src/store/process.ts +22 -0
- package/src/store/schedule.test.ts +59 -0
- package/src/store/schedule.ts +91 -27
- package/src/store/workflow.ts +2 -0
- package/src/workflow-analytics.ts +274 -1
|
@@ -2,11 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
import { ExecutionTokenUsageSchema, ReasoningEffortSchema } from '@llumiverse/common/schemas';
|
|
4
4
|
import { z } from 'zod';
|
|
5
|
+
import { AGENT_RUN_FEEDBACK_COMMENT_MAX_LENGTH, AGENT_RUN_FEEDBACK_ID_MAX_LENGTH } from '../store/agent-run-values.js';
|
|
5
6
|
import type { AgentMessageType, FileProcessingStatus } from '../store/workflow.js';
|
|
6
7
|
import { type AgentEvent, AgentEventType, LlmCallType, TelemetryToolType } from '../workflow-analytics.js';
|
|
7
8
|
import * as AppLifecycleSchemas from './app-lifecycle.js';
|
|
8
9
|
import {
|
|
9
10
|
AgentRunStatusSchema,
|
|
11
|
+
AgentToolApprovalClassSchema,
|
|
10
12
|
ContentObjectTypeRefSchema,
|
|
11
13
|
ConversationActivityStateSchema,
|
|
12
14
|
EventRefSchema,
|
|
@@ -36,6 +38,252 @@ import { AgentCheckpointConfigurationSchema } from './project-configuration.js';
|
|
|
36
38
|
import { nullableStringSchema } from './schema-primitives.js';
|
|
37
39
|
import { InteractionExecutionConfigurationSchema } from './store.js';
|
|
38
40
|
|
|
41
|
+
// ----------------------------------------------------------------------------
|
|
42
|
+
// Evaluation vocabularies
|
|
43
|
+
// ----------------------------------------------------------------------------
|
|
44
|
+
|
|
45
|
+
export const TurnTerminalTypeSchema = z
|
|
46
|
+
.enum(['answer', 'user_stopped', 'completed', 'failed', 'cancelled', 'interrupted'])
|
|
47
|
+
.meta({ id: 'TurnTerminalType', description: 'How an agent turn ended.' });
|
|
48
|
+
|
|
49
|
+
export const EvaluationSeveritySchema = z.enum(['none', 'low', 'medium', 'high']).meta({
|
|
50
|
+
id: 'EvaluationSeverity',
|
|
51
|
+
description: 'Worst deterministic detector level. `none` means no detector fired, not success.',
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
export const TurnEvaluationFlagSchema = z
|
|
55
|
+
.enum([
|
|
56
|
+
'user_stopped_after_failure',
|
|
57
|
+
'mutation_unsuccessful',
|
|
58
|
+
'run_failed',
|
|
59
|
+
'unrecovered_tool',
|
|
60
|
+
'fail_streak',
|
|
61
|
+
'identical_retry',
|
|
62
|
+
'reread',
|
|
63
|
+
'high_gather',
|
|
64
|
+
'overhead',
|
|
65
|
+
'followup_after_answer',
|
|
66
|
+
'approval_denied',
|
|
67
|
+
'circuit_breaker',
|
|
68
|
+
])
|
|
69
|
+
.meta({ id: 'TurnEvaluationFlag', description: 'Reason behind an evaluation severity.' });
|
|
70
|
+
|
|
71
|
+
export const ToolErrorClassSchema = z
|
|
72
|
+
.enum(['schema', 'platform', 'config', 'environment', 'other'])
|
|
73
|
+
.meta({ id: 'ToolErrorClass', description: 'Coarse class of a tool error, derived from the error text.' });
|
|
74
|
+
|
|
75
|
+
const ToolErrorClassCountsSchema = z.strictObject({
|
|
76
|
+
schema: z.number().int().min(0),
|
|
77
|
+
platform: z.number().int().min(0),
|
|
78
|
+
config: z.number().int().min(0),
|
|
79
|
+
environment: z.number().int().min(0),
|
|
80
|
+
other: z.number().int().min(0),
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
const TelemetryDeploymentSchema = z.strictObject({
|
|
84
|
+
env: z.string(),
|
|
85
|
+
group: z.string(),
|
|
86
|
+
version: z.string(),
|
|
87
|
+
region: z.string().optional(),
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
const TelemetryProducerSchema = z.strictObject({
|
|
91
|
+
version: z.string(),
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
export const JudgeGateReasonSchema = z
|
|
95
|
+
.enum(['signal', 'sample'])
|
|
96
|
+
.meta({ id: 'JudgeGateReason', description: 'Why the judge looked at a run.' });
|
|
97
|
+
|
|
98
|
+
export const JudgeOutcomeSchema = z
|
|
99
|
+
.enum(['judged', 'skipped_unarchived', 'failed'])
|
|
100
|
+
.meta({ id: 'JudgeOutcome', description: 'What a judge run produced.' });
|
|
101
|
+
|
|
102
|
+
export const JudgeVerdictSchema = z
|
|
103
|
+
.enum(['success', 'partial', 'failure'])
|
|
104
|
+
.meta({ id: 'JudgeVerdict', description: "The judge's reading of a turn." });
|
|
105
|
+
|
|
106
|
+
// ----------------------------------------------------------------------------
|
|
107
|
+
// User feedback
|
|
108
|
+
// ----------------------------------------------------------------------------
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* How the user rated an agent run's work. Two values on purpose: a rating is a signal, not a score,
|
|
112
|
+
* and a wider scale invites a precision the reader does not have.
|
|
113
|
+
*/
|
|
114
|
+
export const AgentRunFeedbackRatingSchema = z
|
|
115
|
+
.enum(['up', 'down'])
|
|
116
|
+
.meta({ id: 'AgentRunFeedbackRating', description: 'Thumbs up or thumbs down on an agent run.' });
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* The closed reason vocabulary. Closed rather than free text because the reason is the part that
|
|
120
|
+
* gets counted; the free-text `comment` stays with the tenant and is never aggregated.
|
|
121
|
+
*/
|
|
122
|
+
export const AgentRunFeedbackReasonCodeSchema = z
|
|
123
|
+
.enum([
|
|
124
|
+
'accurate',
|
|
125
|
+
'helpful',
|
|
126
|
+
'fast',
|
|
127
|
+
'well_explained',
|
|
128
|
+
'wrong_result',
|
|
129
|
+
'incomplete',
|
|
130
|
+
'misunderstood_request',
|
|
131
|
+
'too_slow',
|
|
132
|
+
'tool_failure',
|
|
133
|
+
'unsafe_action',
|
|
134
|
+
'other',
|
|
135
|
+
])
|
|
136
|
+
.meta({ id: 'AgentRunFeedbackReasonCode', description: 'Why the run was rated the way it was.' });
|
|
137
|
+
|
|
138
|
+
export const AgentRunFeedbackPayloadSchema = z
|
|
139
|
+
.strictObject({
|
|
140
|
+
feedback_id: z.string().min(1).max(AGENT_RUN_FEEDBACK_ID_MAX_LENGTH).meta({
|
|
141
|
+
description: 'Client-generated idempotency key (a UUID). A retried request with the same id is a no-op.',
|
|
142
|
+
}),
|
|
143
|
+
rating: AgentRunFeedbackRatingSchema,
|
|
144
|
+
reason_code: AgentRunFeedbackReasonCodeSchema.optional(),
|
|
145
|
+
comment: z.string().max(AGENT_RUN_FEEDBACK_COMMENT_MAX_LENGTH).optional().meta({
|
|
146
|
+
description: 'Free-text comment. Stored with the tenant; only its presence is ever counted.',
|
|
147
|
+
}),
|
|
148
|
+
message_id: z.string().optional().meta({
|
|
149
|
+
description: 'Rate one message rather than the run as a whole.',
|
|
150
|
+
}),
|
|
151
|
+
message_seq: z.number().int().min(0).optional().meta({
|
|
152
|
+
description: 'Position of the rated message in the run, when a message is rated.',
|
|
153
|
+
}),
|
|
154
|
+
})
|
|
155
|
+
.meta({ id: 'AgentRunFeedbackPayload', description: 'A user rating on an agent run.' });
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* What happened to the rating. `recorded` and `replaced` both mean it counts; `replaced` says an
|
|
159
|
+
* earlier rating by the same user on the same scope was superseded. `disabled` means this
|
|
160
|
+
* deployment does not record feedback; it answers 200 because a rating is never the user's error.
|
|
161
|
+
*/
|
|
162
|
+
export const AgentRunFeedbackStatusSchema = z
|
|
163
|
+
.enum(['recorded', 'replaced', 'disabled'])
|
|
164
|
+
.meta({ id: 'AgentRunFeedbackStatus', description: 'Whether the rating was recorded.' });
|
|
165
|
+
|
|
166
|
+
export const AgentRunFeedbackCountsSchema = z
|
|
167
|
+
.strictObject({
|
|
168
|
+
up: z.number().int().min(0),
|
|
169
|
+
down: z.number().int().min(0),
|
|
170
|
+
last_rating: AgentRunFeedbackRatingSchema.optional(),
|
|
171
|
+
last_reason_code: AgentRunFeedbackReasonCodeSchema.optional(),
|
|
172
|
+
})
|
|
173
|
+
.meta({
|
|
174
|
+
id: 'AgentRunFeedbackCounts',
|
|
175
|
+
description:
|
|
176
|
+
'Ratings over the retained feedback entries, one vote per rater: a user who rated the run and ' +
|
|
177
|
+
'one of its answers counts once, by their latest rating.',
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
export const AgentRunFeedbackResponseSchema = z
|
|
181
|
+
.strictObject({
|
|
182
|
+
status: AgentRunFeedbackStatusSchema,
|
|
183
|
+
feedback_counts: AgentRunFeedbackCountsSchema.optional(),
|
|
184
|
+
})
|
|
185
|
+
.meta({ id: 'AgentRunFeedbackResponse', description: 'Result of rating an agent run.' });
|
|
186
|
+
|
|
187
|
+
export const AgentRunFeedbackEntrySchema = z
|
|
188
|
+
.strictObject({
|
|
189
|
+
feedback_id: z.string(),
|
|
190
|
+
rating: AgentRunFeedbackRatingSchema,
|
|
191
|
+
reason_code: AgentRunFeedbackReasonCodeSchema.optional(),
|
|
192
|
+
comment: z.string().optional(),
|
|
193
|
+
message_id: z.string().optional(),
|
|
194
|
+
message_seq: z.number().int().optional(),
|
|
195
|
+
message_scoped: z.boolean(),
|
|
196
|
+
user_id: z.string(),
|
|
197
|
+
rated_at: z.string().meta({ format: 'date-time' }),
|
|
198
|
+
replaced_at: z.string().meta({ format: 'date-time' }).optional(),
|
|
199
|
+
})
|
|
200
|
+
.meta({ id: 'AgentRunFeedbackEntry', description: 'One recorded rating on an agent run.' });
|
|
201
|
+
|
|
202
|
+
// ----------------------------------------------------------------------------
|
|
203
|
+
// Evaluation summary
|
|
204
|
+
// ----------------------------------------------------------------------------
|
|
205
|
+
|
|
206
|
+
const AgentRunEvaluationTotalsSchema = z.strictObject({
|
|
207
|
+
tool_calls: z.number().int(),
|
|
208
|
+
error_tool_results: z.number().int(),
|
|
209
|
+
unrecovered_tools: z.array(z.string()),
|
|
210
|
+
llm_calls: z.number().int(),
|
|
211
|
+
prompt_tokens: z.number(),
|
|
212
|
+
completion_tokens: z.number(),
|
|
213
|
+
cached_tokens: z.number(),
|
|
214
|
+
retry_completion_tokens: z.number(),
|
|
215
|
+
active_ms: z.number(),
|
|
216
|
+
ask_user_wait_ms: z.number(),
|
|
217
|
+
approval_wait_ms: z.number(),
|
|
218
|
+
followups: z.number().int(),
|
|
219
|
+
approvals_requested: z.number().int(),
|
|
220
|
+
approvals_denied: z.number().int(),
|
|
221
|
+
stop_requests: z.number().int(),
|
|
222
|
+
/** Absent on rollups written before the stall counters existed; read as 0. */
|
|
223
|
+
stall_trips: z.number().int().optional(),
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* What the workflow reports about a run: the fold of its turn evaluations. Owned by the workflow;
|
|
228
|
+
* it never carries feedback or judge fields, which the server owns.
|
|
229
|
+
*/
|
|
230
|
+
export const AgentRunEvaluationRollupSchema = z
|
|
231
|
+
.strictObject({
|
|
232
|
+
seq: z.number().int().min(1).meta({
|
|
233
|
+
description: 'Monotonic per run; the server ignores a rollup older than the one it holds.',
|
|
234
|
+
}),
|
|
235
|
+
detector_version: z.number().int(),
|
|
236
|
+
turns: z.number().int(),
|
|
237
|
+
severity: EvaluationSeveritySchema,
|
|
238
|
+
flags: z.array(TurnEvaluationFlagSchema),
|
|
239
|
+
worst_turn_seq: z.number().int().optional(),
|
|
240
|
+
last_terminal_type: TurnTerminalTypeSchema.optional(),
|
|
241
|
+
terminal_error_class: z.string().optional(),
|
|
242
|
+
partial: z.boolean().optional(),
|
|
243
|
+
totals: AgentRunEvaluationTotalsSchema,
|
|
244
|
+
updated_at: z.string().meta({ format: 'date-time' }),
|
|
245
|
+
})
|
|
246
|
+
.meta({ id: 'AgentRunEvaluationRollup', description: 'Fold of the turn evaluations of a run.' });
|
|
247
|
+
|
|
248
|
+
export const AgentRunJudgeResultSchema = z
|
|
249
|
+
.strictObject({
|
|
250
|
+
rev: z.number().int(),
|
|
251
|
+
gate: JudgeGateReasonSchema,
|
|
252
|
+
sample_rate: z.number(),
|
|
253
|
+
selected_probability: z.number(),
|
|
254
|
+
outcome: JudgeOutcomeSchema,
|
|
255
|
+
verdict: JudgeVerdictSchema.optional(),
|
|
256
|
+
score: z.number().optional(),
|
|
257
|
+
model: z.string().optional(),
|
|
258
|
+
prompt_version: z.string(),
|
|
259
|
+
turns_judged: z.array(z.number().int()).optional(),
|
|
260
|
+
judged_at: z.string().meta({ format: 'date-time' }),
|
|
261
|
+
})
|
|
262
|
+
.meta({ id: 'AgentRunJudgeResult', description: 'Latest judge result for a run.' });
|
|
263
|
+
|
|
264
|
+
export const AgentRunContradictionReasonSchema = z
|
|
265
|
+
.enum(['feedback_down_on_clean_run', 'judge_failure_on_clean_run'])
|
|
266
|
+
.meta({ id: 'AgentRunContradictionReason' });
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* The server-owned evaluation summary of a run: the workflow rollup, the feedback counts and the
|
|
270
|
+
* judge result, plus the derived severity, flags and contradiction the run list filters on.
|
|
271
|
+
*/
|
|
272
|
+
export const AgentRunEvaluationSchema = z
|
|
273
|
+
.strictObject({
|
|
274
|
+
rev: z.number().int().meta({ description: 'Bumped on every change to any part of the summary.' }),
|
|
275
|
+
rollup: AgentRunEvaluationRollupSchema.optional(),
|
|
276
|
+
feedback_counts: AgentRunFeedbackCountsSchema.optional(),
|
|
277
|
+
judge: AgentRunJudgeResultSchema.optional(),
|
|
278
|
+
severity: EvaluationSeveritySchema,
|
|
279
|
+
flags: z.array(TurnEvaluationFlagSchema),
|
|
280
|
+
contradicted: z.boolean(),
|
|
281
|
+
contradiction_reasons: z.array(AgentRunContradictionReasonSchema).optional(),
|
|
282
|
+
deployment_env: z.string().optional(),
|
|
283
|
+
updated_at: z.string().meta({ format: 'date-time' }),
|
|
284
|
+
})
|
|
285
|
+
.meta({ id: 'AgentRunEvaluation', description: 'Evaluation summary of an agent run.' });
|
|
286
|
+
|
|
39
287
|
const agentEventBase = {
|
|
40
288
|
timestamp: z.string(),
|
|
41
289
|
runId: z.string(),
|
|
@@ -46,6 +294,9 @@ const agentEventBase = {
|
|
|
46
294
|
interactionId: z.string(),
|
|
47
295
|
parentRunId: z.string().optional(),
|
|
48
296
|
ancestorRunIds: z.array(z.string()).optional(),
|
|
297
|
+
eventId: z.string().optional(),
|
|
298
|
+
deployment: TelemetryDeploymentSchema.optional(),
|
|
299
|
+
producer: TelemetryProducerSchema.optional(),
|
|
49
300
|
};
|
|
50
301
|
|
|
51
302
|
const AgentRunStartedEventSchema = z.strictObject({
|
|
@@ -123,6 +374,112 @@ const ToolCallEventSchema = z.strictObject({
|
|
|
123
374
|
errorType: z.string().optional(),
|
|
124
375
|
errorMessage: z.string().optional(),
|
|
125
376
|
spawnedChildWorkflow: z.boolean().optional(),
|
|
377
|
+
approvalClass: AgentToolApprovalClassSchema.optional(),
|
|
378
|
+
errorClass: ToolErrorClassSchema.optional(),
|
|
379
|
+
signatureHash: z.string().optional(),
|
|
380
|
+
workstreamId: z.string().optional(),
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
const TurnToolObservationSchema = z.strictObject({
|
|
384
|
+
seq: z.number().int().min(1),
|
|
385
|
+
toolUseId: z.string(),
|
|
386
|
+
name: z.string(),
|
|
387
|
+
approvalClass: AgentToolApprovalClassSchema.optional(),
|
|
388
|
+
ok: z.boolean(),
|
|
389
|
+
errorClass: ToolErrorClassSchema.optional(),
|
|
390
|
+
sig: z.string(),
|
|
391
|
+
durationMs: z.number(),
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
const TurnEvaluationEventSchema = z.strictObject({
|
|
395
|
+
...agentEventBase,
|
|
396
|
+
eventType: z.literal(AgentEventType.TurnEvaluation),
|
|
397
|
+
schemaVersion: z.number().int(),
|
|
398
|
+
detectorVersion: z.number().int(),
|
|
399
|
+
workstreamId: z.string(),
|
|
400
|
+
turnSeq: z.number().int().min(1),
|
|
401
|
+
terminalType: TurnTerminalTypeSchema,
|
|
402
|
+
partial: z.boolean().optional(),
|
|
403
|
+
startedAt: z.string(),
|
|
404
|
+
endedAt: z.string(),
|
|
405
|
+
durationMs: z.number(),
|
|
406
|
+
activeMs: z.number(),
|
|
407
|
+
askUserWaitMs: z.number(),
|
|
408
|
+
approvalWaitMs: z.number(),
|
|
409
|
+
timeToFirstAnswerMs: z.number().optional(),
|
|
410
|
+
toolCalls: z.number().int(),
|
|
411
|
+
errorToolResults: z.number().int(),
|
|
412
|
+
maxFailStreak: z.number().int(),
|
|
413
|
+
identicalRetryCount: z.number().int(),
|
|
414
|
+
unrecoveredTools: z.array(z.string()),
|
|
415
|
+
errorClasses: ToolErrorClassCountsSchema,
|
|
416
|
+
gatherCalls: z.number().int(),
|
|
417
|
+
gatherRatio: z.number().optional(),
|
|
418
|
+
mutationAttempted: z.boolean(),
|
|
419
|
+
mutationSucceeded: z.boolean(),
|
|
420
|
+
rereadCount: z.number().int(),
|
|
421
|
+
overheadCalls: z.number().int(),
|
|
422
|
+
skillsLoaded: z.number().int(),
|
|
423
|
+
interruptedToolCalls: z.number().int(),
|
|
424
|
+
llmCalls: z.number().int(),
|
|
425
|
+
promptTokens: z.number(),
|
|
426
|
+
completionTokens: z.number(),
|
|
427
|
+
cachedTokens: z.number(),
|
|
428
|
+
retryCompletionTokens: z.number(),
|
|
429
|
+
approvalsRequested: z.number().int(),
|
|
430
|
+
approvalsDenied: z.number().int(),
|
|
431
|
+
stopRequests: z.number().int(),
|
|
432
|
+
/** Absent on events from producers that predate the stall counters; read as 0. */
|
|
433
|
+
stallCorrectives: z.number().int().optional(),
|
|
434
|
+
stallTrips: z.number().int().optional(),
|
|
435
|
+
followupAfterAnswer: z.boolean(),
|
|
436
|
+
severity: EvaluationSeveritySchema,
|
|
437
|
+
flags: z.array(TurnEvaluationFlagSchema),
|
|
438
|
+
terminalErrorClass: z.string().optional(),
|
|
439
|
+
tools: z.array(TurnToolObservationSchema),
|
|
440
|
+
toolsTruncated: z.number().int(),
|
|
441
|
+
});
|
|
442
|
+
|
|
443
|
+
const FeedbackEventSchema = z.strictObject({
|
|
444
|
+
...agentEventBase,
|
|
445
|
+
eventType: z.literal(AgentEventType.Feedback),
|
|
446
|
+
feedbackId: z.string(),
|
|
447
|
+
rating: AgentRunFeedbackRatingSchema,
|
|
448
|
+
reasonCode: AgentRunFeedbackReasonCodeSchema.optional(),
|
|
449
|
+
hasComment: z.boolean(),
|
|
450
|
+
messageScoped: z.boolean(),
|
|
451
|
+
messageSeq: z.number().int().optional(),
|
|
452
|
+
replaced: z.boolean(),
|
|
453
|
+
});
|
|
454
|
+
|
|
455
|
+
const TurnJudgementEventSchema = z.strictObject({
|
|
456
|
+
...agentEventBase,
|
|
457
|
+
eventType: z.literal(AgentEventType.TurnJudgement),
|
|
458
|
+
evaluationRev: z.number().int(),
|
|
459
|
+
workstreamId: z.string(),
|
|
460
|
+
turnSeq: z.number().int().min(0),
|
|
461
|
+
gate: JudgeGateReasonSchema,
|
|
462
|
+
sampleRate: z.number(),
|
|
463
|
+
selectedProbability: z.number(),
|
|
464
|
+
outcome: JudgeOutcomeSchema,
|
|
465
|
+
verdict: JudgeVerdictSchema.optional(),
|
|
466
|
+
score: z.number().optional(),
|
|
467
|
+
reasons: z.array(z.string()).optional(),
|
|
468
|
+
promptVersion: z.string(),
|
|
469
|
+
detectorVersion: z.number().int().optional(),
|
|
470
|
+
});
|
|
471
|
+
|
|
472
|
+
const StallBreakerEventSchema = z.strictObject({
|
|
473
|
+
...agentEventBase,
|
|
474
|
+
eventType: z.literal(AgentEventType.StallBreaker),
|
|
475
|
+
action: z.enum(['corrective', 'trip']),
|
|
476
|
+
toolNames: z.array(z.string()),
|
|
477
|
+
repeatCount: z.number().int(),
|
|
478
|
+
stallMeasure: z.number().int(),
|
|
479
|
+
allErrored: z.boolean(),
|
|
480
|
+
iteration: z.number().int(),
|
|
481
|
+
interactive: z.boolean(),
|
|
482
|
+
workstreamId: z.string(),
|
|
126
483
|
});
|
|
127
484
|
|
|
128
485
|
export const AgentEventSchema: z.ZodType<AgentEvent> = z
|
|
@@ -131,6 +488,10 @@ export const AgentEventSchema: z.ZodType<AgentEvent> = z
|
|
|
131
488
|
AgentRunCompletedEventSchema,
|
|
132
489
|
LlmCallEventSchema,
|
|
133
490
|
ToolCallEventSchema,
|
|
491
|
+
TurnEvaluationEventSchema,
|
|
492
|
+
FeedbackEventSchema,
|
|
493
|
+
TurnJudgementEventSchema,
|
|
494
|
+
StallBreakerEventSchema,
|
|
134
495
|
])
|
|
135
496
|
.meta({ id: 'AgentEvent' });
|
|
136
497
|
|
|
@@ -420,6 +781,11 @@ export const AutonomousRunResponseSchema = z
|
|
|
420
781
|
.array(z.string())
|
|
421
782
|
.meta({ description: 'Lessons learned from the conversation (extracted at completion)' })
|
|
422
783
|
.optional(),
|
|
784
|
+
evaluation: AgentRunEvaluationSchema.meta({ description: 'Evaluation summary of the run.' }).optional(),
|
|
785
|
+
feedback: z
|
|
786
|
+
.array(AgentRunFeedbackEntrySchema)
|
|
787
|
+
.meta({ description: 'Retained user ratings on the run.' })
|
|
788
|
+
.optional(),
|
|
423
789
|
archived_at: z
|
|
424
790
|
.string()
|
|
425
791
|
.meta({ description: 'When the last successful archive completed', format: 'date-time' })
|
|
@@ -568,6 +934,11 @@ export const AgentRunSchema = z
|
|
|
568
934
|
.array(z.string())
|
|
569
935
|
.meta({ description: 'Lessons learned from the conversation (extracted at completion)' })
|
|
570
936
|
.optional(),
|
|
937
|
+
evaluation: AgentRunEvaluationSchema.meta({ description: 'Evaluation summary of the run.' }).optional(),
|
|
938
|
+
feedback: z
|
|
939
|
+
.array(AgentRunFeedbackEntrySchema)
|
|
940
|
+
.meta({ description: 'Retained user ratings on the run.' })
|
|
941
|
+
.optional(),
|
|
571
942
|
archived_at: z
|
|
572
943
|
.string()
|
|
573
944
|
.meta({ description: 'When the last successful archive completed', format: 'date-time' })
|
|
@@ -988,6 +1359,14 @@ export const AgentRunArtifactQuerySchema = z
|
|
|
988
1359
|
|
|
989
1360
|
export const AgentRunDetailsQuerySchema = z
|
|
990
1361
|
.object({
|
|
1362
|
+
from: z
|
|
1363
|
+
.string()
|
|
1364
|
+
.max(12000)
|
|
1365
|
+
.meta({
|
|
1366
|
+
description:
|
|
1367
|
+
'Opaque history cursor from next_from; requires include_history. Invalid or expired cursors return a snapshot.',
|
|
1368
|
+
})
|
|
1369
|
+
.optional(),
|
|
991
1370
|
include_history: z.boolean().optional(),
|
|
992
1371
|
hydrate_payloads: z.boolean().optional(),
|
|
993
1372
|
})
|
|
@@ -999,6 +1378,10 @@ export const AgentRunArtifactsQuerySchema = z
|
|
|
999
1378
|
})
|
|
1000
1379
|
.meta({ id: 'AgentRunArtifactsQuery' });
|
|
1001
1380
|
|
|
1381
|
+
export const ListAgentRunsEvaluationSeveritySchema = z
|
|
1382
|
+
.enum(['unrated', 'none', 'low', 'medium', 'high'])
|
|
1383
|
+
.meta({ id: 'ListAgentRunsEvaluationSeverity' });
|
|
1384
|
+
|
|
1002
1385
|
export const ListAgentRunsQuerySchema = z
|
|
1003
1386
|
.object({
|
|
1004
1387
|
id: z.string().meta({ description: 'Filter by agent run ID' }).optional(),
|
|
@@ -1022,6 +1405,21 @@ export const ListAgentRunsQuerySchema = z
|
|
|
1022
1405
|
run_kind: RunKindSchema.meta({ description: 'Filter by internal run discriminator' }).optional(),
|
|
1023
1406
|
sort: z.enum(['started_at', 'updated_at']).meta({ description: 'Field to sort by' }).optional(),
|
|
1024
1407
|
order: z.enum(['asc', 'desc']).meta({ description: 'Sort order' }).optional(),
|
|
1408
|
+
evaluation_severity: z
|
|
1409
|
+
.array(ListAgentRunsEvaluationSeveritySchema)
|
|
1410
|
+
.meta({ description: 'Filter by evaluation severity; `unrated` selects runs without an evaluation' })
|
|
1411
|
+
.optional(),
|
|
1412
|
+
evaluation_flag: z
|
|
1413
|
+
.array(TurnEvaluationFlagSchema)
|
|
1414
|
+
.meta({ description: 'Filter by evaluation flag (any of)' })
|
|
1415
|
+
.optional(),
|
|
1416
|
+
feedback_rating: AgentRunFeedbackRatingSchema.meta({
|
|
1417
|
+
description: 'Filter by last feedback rating',
|
|
1418
|
+
}).optional(),
|
|
1419
|
+
contradicted: z
|
|
1420
|
+
.boolean()
|
|
1421
|
+
.meta({ description: 'Only runs whose feedback or judge contradicts the detectors' })
|
|
1422
|
+
.optional(),
|
|
1025
1423
|
})
|
|
1026
1424
|
.meta({ id: 'ListAgentRunsQuery' });
|
|
1027
1425
|
|
|
@@ -1107,6 +1505,7 @@ export const UpdateAgentRunStatusPayloadSchema = z
|
|
|
1107
1505
|
last_archive_error: z.string().optional(),
|
|
1108
1506
|
sequence: z.number().optional(),
|
|
1109
1507
|
process_state: ProcessStateSchema.optional(),
|
|
1508
|
+
evaluation_rollup: AgentRunEvaluationRollupSchema.optional(),
|
|
1110
1509
|
})
|
|
1111
1510
|
.meta({ id: 'UpdateAgentRunStatusPayload' });
|
|
1112
1511
|
|
|
@@ -207,3 +207,34 @@ export type ApiKeyReadResponseFromSchema = z.infer<typeof ApiKeyReadResponseSche
|
|
|
207
207
|
export type CreateApiKeyPayloadFromSchema = z.infer<typeof CreateApiKeyPayloadSchema>;
|
|
208
208
|
export type UpdateApiKeyPayloadFromSchema = z.infer<typeof UpdateApiKeyPayloadSchema>;
|
|
209
209
|
export type AuthTokenResponseFromSchema = z.infer<typeof AuthTokenResponseSchema>;
|
|
210
|
+
|
|
211
|
+
/** Project-independent organization credentials, managed only by interactive administrators. */
|
|
212
|
+
export const AccountApiKeySchema = ApiKeySchema.pick({
|
|
213
|
+
id: true,
|
|
214
|
+
name: true,
|
|
215
|
+
account: true,
|
|
216
|
+
enabled: true,
|
|
217
|
+
maskedValue: true,
|
|
218
|
+
created_by: true,
|
|
219
|
+
updated_by: true,
|
|
220
|
+
created_at: true,
|
|
221
|
+
updated_at: true,
|
|
222
|
+
expires_at: true,
|
|
223
|
+
})
|
|
224
|
+
.extend({
|
|
225
|
+
scope: z.literal('account'),
|
|
226
|
+
profile: z.literal('account_admin_v1'),
|
|
227
|
+
last_used_at: z.string().meta({ format: 'date-time' }).optional(),
|
|
228
|
+
})
|
|
229
|
+
.meta({ id: 'AccountApiKey' });
|
|
230
|
+
export const AccountApiKeyWithValueSchema = AccountApiKeySchema.extend({ value: z.string() }).meta({
|
|
231
|
+
id: 'AccountApiKeyWithValue',
|
|
232
|
+
});
|
|
233
|
+
export const AccountApiKeyArraySchema = z.array(AccountApiKeySchema).meta({ id: 'AccountApiKeyArray' });
|
|
234
|
+
export const CreateAccountApiKeyPayloadSchema = AccountApiKeySchema.pick({ name: true, expires_at: true })
|
|
235
|
+
.extend({ name: z.string().trim().min(1).regex(/\S/) })
|
|
236
|
+
.meta({ id: 'CreateAccountApiKeyPayload' });
|
|
237
|
+
export const UpdateAccountApiKeyPayloadSchema = AccountApiKeySchema.pick({ name: true, enabled: true })
|
|
238
|
+
.extend({ name: z.string().trim().min(1).regex(/\S/) })
|
|
239
|
+
.partial()
|
|
240
|
+
.meta({ id: 'UpdateAccountApiKeyPayload' });
|
|
@@ -1,37 +1,16 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
2
|
import { gzipSync } from 'node:zlib';
|
|
3
3
|
import { describe, expect, it } from 'vitest';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* `lib/index.js` is bundled wholesale into `lib/vertesia-common.js` by rolldown, and
|
|
10
|
-
* `apps/composable-ui/build/shared-libs.ts` serves that single file to the browser mapped to the
|
|
11
|
-
* bare specifier `@vertesia/common`. There is no per-consumer tree-shaking anywhere on that path,
|
|
12
|
-
* so `import type` erasure in app code cannot save us: anything runtime-reachable from the root
|
|
13
|
-
* barrel ships to every UI user.
|
|
14
|
-
*
|
|
15
|
-
* The rule this enforces: the root may re-export schema-derived TYPES (`export type`, erased by
|
|
16
|
-
* tsc) but must never runtime-export `./api-schemas/*`. `@vertesia/client` is covered too — it is
|
|
17
|
-
* shipped to the browser by the same mechanism, so a schema import there leaks just as badly. Its
|
|
18
|
-
* default and Node.js entries are checked independently because this is also an SDK dependency
|
|
19
|
-
* boundary for Node.js, serverless, CLI, and other JavaScript applications—not only a browser-size
|
|
20
|
-
* concern.
|
|
21
|
-
*
|
|
22
|
-
* Runs against build output, so each artifact is skipped when it has not been built.
|
|
6
|
+
* Keep schema-authoring code out of the common browser bundle and public root entry.
|
|
7
|
+
* Build outputs are required; Turbo builds this package before running its tests.
|
|
8
|
+
* Client packaging is tested by the client package.
|
|
23
9
|
*/
|
|
24
10
|
const path = (relative: string) => new URL(relative, import.meta.url).pathname;
|
|
25
11
|
|
|
26
12
|
const COMMON_BUNDLE = path('../../lib/vertesia-common.js');
|
|
27
13
|
const COMMON_ENTRY = path('../../lib/index.js');
|
|
28
|
-
const CLIENT_BUNDLE = path('../../../client/lib/vertesia-client.js');
|
|
29
|
-
/** `.` and `./node` in the client's exports map — the latter is `lib/nodejs/index.js`, not `lib/index.js`. */
|
|
30
|
-
const CLIENT_ENTRIES = [
|
|
31
|
-
{ label: 'client default entry (.)', file: path('../../../client/lib/index.js') },
|
|
32
|
-
{ label: 'client node entry (./node)', file: path('../../../client/lib/nodejs/index.js') },
|
|
33
|
-
];
|
|
34
|
-
|
|
35
14
|
/** Markers that prove a runtime schema import leaked into a browser artifact. */
|
|
36
15
|
const FORBIDDEN = ['zod', '_zod', 'ZodObject', 'api-schemas', 'toOpenApiComponents', 'ApiSchemaComponents'];
|
|
37
16
|
|
|
@@ -45,7 +24,7 @@ function expectNoSchemaRuntime(file: string, label: string): void {
|
|
|
45
24
|
}
|
|
46
25
|
}
|
|
47
26
|
|
|
48
|
-
describe
|
|
27
|
+
describe('@vertesia/common runtime schema isolation', () => {
|
|
49
28
|
it('does not runtime-reference the api-schemas subpath from the root entry', () => {
|
|
50
29
|
expect(readFileSync(COMMON_ENTRY, 'utf8')).not.toContain('api-schemas');
|
|
51
30
|
});
|
|
@@ -63,18 +42,3 @@ describe.skipIf(!existsSync(COMMON_BUNDLE))('@vertesia/common runtime schema iso
|
|
|
63
42
|
).toBeLessThan(MAX_COMMON_GZIP_BYTES);
|
|
64
43
|
});
|
|
65
44
|
});
|
|
66
|
-
|
|
67
|
-
describe.skipIf(!existsSync(CLIENT_BUNDLE))('@vertesia/client consumer runtime isolation', () => {
|
|
68
|
-
it('contains no zod import or schema registry code', () => {
|
|
69
|
-
// The client is served to the browser the same way common is, so importing
|
|
70
|
-
// `@vertesia/common/api-schemas` from an SDK method would ship zod to every UI user.
|
|
71
|
-
expectNoSchemaRuntime(CLIENT_BUNDLE, 'client browser bundle');
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
it.each(CLIENT_ENTRIES)('does not reference the api-schemas subpath from the $label', ({ label, file }) => {
|
|
75
|
-
// Asserted rather than skipped: a missing artifact means the entry moved or stopped being
|
|
76
|
-
// built, and silently passing would let a packaging regression through unnoticed.
|
|
77
|
-
expect(existsSync(file), `${label} not found at ${file} — has the exports map changed?`).toBe(true);
|
|
78
|
-
expect(readFileSync(file, 'utf8'), `${label} must not import api-schemas`).not.toContain('api-schemas');
|
|
79
|
-
});
|
|
80
|
-
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
const id = z.string().regex(/^[a-f0-9]{24}$/i);
|
|
4
|
+
export const CreateDelegationGrantPayloadSchema = z
|
|
5
|
+
.strictObject({
|
|
6
|
+
schedule_id: id,
|
|
7
|
+
subject: id,
|
|
8
|
+
output_collection_id: id,
|
|
9
|
+
policy_hash: z.string().regex(/^[a-f0-9]{64}$/),
|
|
10
|
+
expires_at: z.string().datetime().nullable(),
|
|
11
|
+
})
|
|
12
|
+
.meta({ id: 'CreateDelegationGrantPayload' });
|
|
13
|
+
export const DelegationGrantSchema = CreateDelegationGrantPayloadSchema.extend({
|
|
14
|
+
id,
|
|
15
|
+
account_id: id,
|
|
16
|
+
project_id: id,
|
|
17
|
+
granter: id,
|
|
18
|
+
created_at: z.string().datetime(),
|
|
19
|
+
revoked_at: z.string().datetime().nullable(),
|
|
20
|
+
}).meta({ id: 'DelegationGrant' });
|
|
21
|
+
export const DelegationGrantArraySchema = z.array(DelegationGrantSchema).meta({ id: 'DelegationGrantArray' });
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { toOpenApiComponents } from './adapter.js';
|
|
4
|
+
import { emitJsonSchema } from './emit-json-schema.js';
|
|
5
|
+
|
|
6
|
+
describe('explicit union emission', () => {
|
|
7
|
+
it('preserves optional explicit unions and nullable schemas', () => {
|
|
8
|
+
const schema = z.object({
|
|
9
|
+
explicit: z.union([z.string(), z.null()]).optional().describe('Clear with null'),
|
|
10
|
+
nullable: z.string().nullable(),
|
|
11
|
+
});
|
|
12
|
+
const emitted = emitJsonSchema(schema);
|
|
13
|
+
expect(emitted.properties).toEqual({
|
|
14
|
+
explicit: { anyOf: [{ type: 'string' }, { type: 'null' }], description: 'Clear with null' },
|
|
15
|
+
nullable: { anyOf: [{ type: 'string' }, { type: 'null' }] },
|
|
16
|
+
});
|
|
17
|
+
expect(JSON.stringify(emitted)).not.toContain('x-vertesia-explicit-union');
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('preserves referenced explicit unions inside named definitions', () => {
|
|
21
|
+
const value = z.union([z.string(), z.boolean(), z.number(), z.null()]).meta({ id: 'Value' });
|
|
22
|
+
const schema = z.object({ value }).meta({ id: 'Holder' });
|
|
23
|
+
const components = toOpenApiComponents({ Holder: emitJsonSchema(schema) });
|
|
24
|
+
expect(components.Value).toEqual({
|
|
25
|
+
anyOf: [{ type: 'string' }, { type: 'boolean' }, { type: 'number' }, { type: 'null' }],
|
|
26
|
+
});
|
|
27
|
+
expect(JSON.stringify(components)).not.toContain('x-vertesia-explicit-union');
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('retains constraints on union branches', () => {
|
|
31
|
+
expect(emitJsonSchema(z.union([z.string().min(2), z.null()])).anyOf).toEqual([
|
|
32
|
+
{ type: 'string', minLength: 2 },
|
|
33
|
+
{ type: 'null' },
|
|
34
|
+
]);
|
|
35
|
+
});
|
|
36
|
+
});
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { isPlainObject, type JsonObject } from './adapter.js';
|
|
3
|
+
|
|
4
|
+
const EXPLICIT_UNION = 'x-vertesia-explicit-union';
|
|
5
|
+
|
|
6
|
+
/** Preserve the published explicit-union spelling across Zod's final compaction pass. */
|
|
7
|
+
export function emitJsonSchema(schema: z.ZodType): JsonObject {
|
|
8
|
+
const emitted = z.toJSONSchema(schema, {
|
|
9
|
+
target: 'draft-2020-12',
|
|
10
|
+
io: 'input',
|
|
11
|
+
override: ({ jsonSchema }) => {
|
|
12
|
+
// Zod 4.5 compacts bare anyOf branches AFTER override runs. Carry the original
|
|
13
|
+
// branches through that pass; optional wrappers inherit this metadata as well.
|
|
14
|
+
const branches = jsonSchema.anyOf;
|
|
15
|
+
if (
|
|
16
|
+
branches?.every(
|
|
17
|
+
(branch) => isPlainObject(branch) && Object.keys(branch).length === 1 && 'type' in branch,
|
|
18
|
+
)
|
|
19
|
+
) {
|
|
20
|
+
jsonSchema[EXPLICIT_UNION] = branches;
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
restoreExplicitUnions(emitted);
|
|
25
|
+
return emitted;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function restoreExplicitUnions(value: unknown): void {
|
|
29
|
+
if (Array.isArray(value)) {
|
|
30
|
+
for (const child of value) restoreExplicitUnions(child);
|
|
31
|
+
} else if (isPlainObject(value)) {
|
|
32
|
+
const branches = value[EXPLICIT_UNION];
|
|
33
|
+
if (Array.isArray(branches)) {
|
|
34
|
+
delete value[EXPLICIT_UNION];
|
|
35
|
+
delete value.type;
|
|
36
|
+
value.anyOf = branches;
|
|
37
|
+
}
|
|
38
|
+
for (const child of Object.values(value)) restoreExplicitUnions(child);
|
|
39
|
+
}
|
|
40
|
+
}
|