@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.
Files changed (179) hide show
  1. package/lib/access-control-values.d.ts +13 -0
  2. package/lib/access-control-values.d.ts.map +1 -1
  3. package/lib/access-control-values.js +24 -0
  4. package/lib/access-control-values.js.map +1 -1
  5. package/lib/api-contract/components.generated.json +4446 -2194
  6. package/lib/api-schemas/account.d.ts +3 -10
  7. package/lib/api-schemas/account.d.ts.map +1 -1
  8. package/lib/api-schemas/account.js +5 -9
  9. package/lib/api-schemas/account.js.map +1 -1
  10. package/lib/api-schemas/adapter.d.ts.map +1 -1
  11. package/lib/api-schemas/adapter.js +8 -1
  12. package/lib/api-schemas/adapter.js.map +1 -1
  13. package/lib/api-schemas/agent-communication.d.ts +1 -0
  14. package/lib/api-schemas/agent-communication.d.ts.map +1 -1
  15. package/lib/api-schemas/agent-communication.js +6 -0
  16. package/lib/api-schemas/agent-communication.js.map +1 -1
  17. package/lib/api-schemas/agent-runs.d.ts +5286 -903
  18. package/lib/api-schemas/agent-runs.d.ts.map +1 -1
  19. package/lib/api-schemas/agent-runs.js +366 -1
  20. package/lib/api-schemas/agent-runs.js.map +1 -1
  21. package/lib/api-schemas/apikey.d.ts +55 -0
  22. package/lib/api-schemas/apikey.d.ts.map +1 -1
  23. package/lib/api-schemas/apikey.js +30 -0
  24. package/lib/api-schemas/apikey.js.map +1 -1
  25. package/lib/api-schemas/app-runtime.d.ts +1240 -22
  26. package/lib/api-schemas/app-runtime.d.ts.map +1 -1
  27. package/lib/api-schemas/content.d.ts +435 -0
  28. package/lib/api-schemas/content.d.ts.map +1 -1
  29. package/lib/api-schemas/delegation.d.ts +35 -0
  30. package/lib/api-schemas/delegation.d.ts.map +1 -0
  31. package/lib/api-schemas/delegation.js +21 -0
  32. package/lib/api-schemas/delegation.js.map +1 -0
  33. package/lib/api-schemas/document-processing.d.ts +261 -0
  34. package/lib/api-schemas/document-processing.d.ts.map +1 -1
  35. package/lib/api-schemas/emit-json-schema.d.ts +5 -0
  36. package/lib/api-schemas/emit-json-schema.d.ts.map +1 -0
  37. package/lib/api-schemas/emit-json-schema.js +37 -0
  38. package/lib/api-schemas/emit-json-schema.js.map +1 -0
  39. package/lib/api-schemas/environment.d.ts +18 -0
  40. package/lib/api-schemas/environment.d.ts.map +1 -1
  41. package/lib/api-schemas/events.d.ts +783 -0
  42. package/lib/api-schemas/events.d.ts.map +1 -1
  43. package/lib/api-schemas/index.d.ts +1 -0
  44. package/lib/api-schemas/index.d.ts.map +1 -1
  45. package/lib/api-schemas/index.js +1 -0
  46. package/lib/api-schemas/index.js.map +1 -1
  47. package/lib/api-schemas/interaction.d.ts +3939 -91
  48. package/lib/api-schemas/interaction.d.ts.map +1 -1
  49. package/lib/api-schemas/interaction.js +14 -3
  50. package/lib/api-schemas/interaction.js.map +1 -1
  51. package/lib/api-schemas/oauth-server.d.ts +1 -0
  52. package/lib/api-schemas/oauth-server.d.ts.map +1 -1
  53. package/lib/api-schemas/oauth-server.js +3 -0
  54. package/lib/api-schemas/oauth-server.js.map +1 -1
  55. package/lib/api-schemas/process-agent-policy.d.ts +69 -0
  56. package/lib/api-schemas/process-agent-policy.d.ts.map +1 -0
  57. package/lib/api-schemas/process-agent-policy.js +154 -0
  58. package/lib/api-schemas/process-agent-policy.js.map +1 -0
  59. package/lib/api-schemas/process.d.ts +1260 -0
  60. package/lib/api-schemas/process.d.ts.map +1 -1
  61. package/lib/api-schemas/process.js +29 -1
  62. package/lib/api-schemas/process.js.map +1 -1
  63. package/lib/api-schemas/project-configuration.d.ts +883 -8
  64. package/lib/api-schemas/project-configuration.d.ts.map +1 -1
  65. package/lib/api-schemas/project-configuration.js +3 -2
  66. package/lib/api-schemas/project-configuration.js.map +1 -1
  67. package/lib/api-schemas/project.d.ts +1330 -22
  68. package/lib/api-schemas/project.d.ts.map +1 -1
  69. package/lib/api-schemas/registry.d.ts +24347 -5694
  70. package/lib/api-schemas/registry.d.ts.map +1 -1
  71. package/lib/api-schemas/registry.js +50 -8
  72. package/lib/api-schemas/registry.js.map +1 -1
  73. package/lib/api-schemas/store.d.ts +4427 -77
  74. package/lib/api-schemas/store.d.ts.map +1 -1
  75. package/lib/api-schemas/sts.d.ts +6 -0
  76. package/lib/api-schemas/sts.d.ts.map +1 -1
  77. package/lib/api-schemas/sts.js +3 -0
  78. package/lib/api-schemas/sts.js.map +1 -1
  79. package/lib/api-schemas/workflow-runs.d.ts +189 -0
  80. package/lib/api-schemas/workflow-runs.d.ts.map +1 -1
  81. package/lib/api-schemas/workflow-runs.js +14 -0
  82. package/lib/api-schemas/workflow-runs.js.map +1 -1
  83. package/lib/apikey.d.ts +8 -0
  84. package/lib/apikey.d.ts.map +1 -1
  85. package/lib/apikey.js.map +1 -1
  86. package/lib/delegation.d.ts +13 -0
  87. package/lib/delegation.d.ts.map +1 -0
  88. package/lib/delegation.js +2 -0
  89. package/lib/delegation.js.map +1 -0
  90. package/lib/environment.d.ts +1 -0
  91. package/lib/environment.d.ts.map +1 -1
  92. package/lib/index.d.ts +1 -0
  93. package/lib/index.d.ts.map +1 -1
  94. package/lib/index.js.map +1 -1
  95. package/lib/oauth-scopes.d.ts.map +1 -1
  96. package/lib/oauth-scopes.js +3 -1
  97. package/lib/oauth-scopes.js.map +1 -1
  98. package/lib/project-values.d.ts +1 -0
  99. package/lib/project-values.d.ts.map +1 -1
  100. package/lib/project-values.js +1 -0
  101. package/lib/project-values.js.map +1 -1
  102. package/lib/store/agent-run-values.d.ts +25 -0
  103. package/lib/store/agent-run-values.d.ts.map +1 -0
  104. package/lib/store/agent-run-values.js +25 -0
  105. package/lib/store/agent-run-values.js.map +1 -0
  106. package/lib/store/agent-run.d.ts +22 -2
  107. package/lib/store/agent-run.d.ts.map +1 -1
  108. package/lib/store/agent-run.js +1 -1
  109. package/lib/store/agent-run.js.map +1 -1
  110. package/lib/store/conversation-state.d.ts +1 -1
  111. package/lib/store/conversation-state.d.ts.map +1 -1
  112. package/lib/store/intake-policy-schema.generated.d.ts.map +1 -1
  113. package/lib/store/intake-policy-schema.generated.js +124 -0
  114. package/lib/store/intake-policy-schema.generated.js.map +1 -1
  115. package/lib/store/process.d.ts +17 -0
  116. package/lib/store/process.d.ts.map +1 -1
  117. package/lib/store/process.js.map +1 -1
  118. package/lib/store/schedule.d.ts +62 -25
  119. package/lib/store/schedule.d.ts.map +1 -1
  120. package/lib/store/schedule.js +2 -2
  121. package/lib/store/schedule.js.map +1 -1
  122. package/lib/store/workflow.d.ts +2 -0
  123. package/lib/store/workflow.d.ts.map +1 -1
  124. package/lib/store/workflow.js.map +1 -1
  125. package/lib/vertesia-common.js +2 -2
  126. package/lib/vertesia-common.js.map +1 -1
  127. package/lib/workflow-analytics.d.ts +230 -2
  128. package/lib/workflow-analytics.d.ts.map +1 -1
  129. package/lib/workflow-analytics.js +8 -0
  130. package/lib/workflow-analytics.js.map +1 -1
  131. package/package.json +8 -8
  132. package/src/access-control-values.ts +26 -0
  133. package/src/api-contract/components.contract.test.ts +2 -2
  134. package/src/api-contract/components.generated.json +4446 -2194
  135. package/src/api-schemas/account-apikey.test.ts +60 -0
  136. package/src/api-schemas/account.contract.test.ts +2 -1
  137. package/src/api-schemas/account.ts +5 -9
  138. package/src/api-schemas/adapter-edge-cases.test.ts +19 -0
  139. package/src/api-schemas/adapter.ts +8 -1
  140. package/src/api-schemas/agent-communication.ts +6 -0
  141. package/src/api-schemas/agent-history.contract.test.ts +42 -0
  142. package/src/api-schemas/agent-runs.contract.test.ts +240 -0
  143. package/src/api-schemas/agent-runs.ts +399 -0
  144. package/src/api-schemas/apikey.ts +31 -0
  145. package/src/api-schemas/bundle-isolation.test.ts +5 -41
  146. package/src/api-schemas/delegation.ts +21 -0
  147. package/src/api-schemas/emit-json-schema.test.ts +36 -0
  148. package/src/api-schemas/emit-json-schema.ts +40 -0
  149. package/src/api-schemas/environment.contract.test.ts +3 -0
  150. package/src/api-schemas/index.ts +1 -0
  151. package/src/api-schemas/interaction.contract.test.ts +23 -0
  152. package/src/api-schemas/interaction.ts +15 -3
  153. package/src/api-schemas/llumiverse.contract.test.ts +3 -9
  154. package/src/api-schemas/oauth-server.ts +4 -0
  155. package/src/api-schemas/process-agent-policy.ts +167 -0
  156. package/src/api-schemas/process.contract.test.ts +115 -0
  157. package/src/api-schemas/process.ts +31 -1
  158. package/src/api-schemas/project-configuration.ts +3 -2
  159. package/src/api-schemas/project.contract.test.ts +15 -0
  160. package/src/api-schemas/registry.ts +59 -10
  161. package/src/api-schemas/store.contract.test.ts +4 -126
  162. package/src/api-schemas/sts.ts +3 -0
  163. package/src/api-schemas/workflow-runs.ts +16 -0
  164. package/src/api-schemas/zeno-response-contracts.test.ts +36 -0
  165. package/src/apikey.ts +13 -0
  166. package/src/delegation.ts +18 -0
  167. package/src/index.ts +6 -0
  168. package/src/oauth-scopes.ts +3 -1
  169. package/src/project-values.ts +1 -0
  170. package/src/roles.test.ts +1 -0
  171. package/src/store/agent-run-values.ts +27 -0
  172. package/src/store/agent-run.ts +36 -0
  173. package/src/store/conversation-state.ts +1 -1
  174. package/src/store/intake-policy-schema.generated.ts +125 -0
  175. package/src/store/process.ts +22 -0
  176. package/src/store/schedule.test.ts +59 -0
  177. package/src/store/schedule.ts +91 -27
  178. package/src/store/workflow.ts +2 -0
  179. package/src/workflow-analytics.ts +274 -1
@@ -0,0 +1,60 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { ApiSchemaComponents } from '../api-contract/index.js';
3
+ import {
4
+ AccountApiKeySchema,
5
+ AccountApiKeyWithValueSchema,
6
+ ApiKeySchema,
7
+ CreateAccountApiKeyPayloadSchema,
8
+ UpdateAccountApiKeyPayloadSchema,
9
+ } from './apikey.js';
10
+ import { validateApiRequest } from './registry.js';
11
+
12
+ describe('separate account-key wire contract', () => {
13
+ const metadata = {
14
+ id: 'key',
15
+ name: 'IdP',
16
+ account: 'account',
17
+ scope: 'account',
18
+ profile: 'account_admin_v1',
19
+ enabled: true,
20
+ maskedValue: 'sk-…',
21
+ created_at: '2026-09-15T00:00:00Z',
22
+ updated_at: '2026-09-15T00:00:00Z',
23
+ created_by: 'user:admin',
24
+ updated_by: 'user:admin',
25
+ };
26
+ it('requires account scope/profile without weakening the project-key contract', () => {
27
+ expect(AccountApiKeySchema.safeParse(metadata).success).toBe(true);
28
+ expect(AccountApiKeySchema.safeParse({ ...metadata, profile: 'other' }).success).toBe(false);
29
+ expect(ApiKeySchema.safeParse(metadata).success).toBe(false);
30
+ expect(AccountApiKeyWithValueSchema.safeParse(metadata).success).toBe(false);
31
+ expect(AccountApiKeyWithValueSchema.safeParse({ ...metadata, value: 'test-secret' }).success).toBe(true);
32
+ });
33
+ it('publishes strict management payloads and metadata without a value or project property', () => {
34
+ const components = ApiSchemaComponents as Record<
35
+ string,
36
+ { additionalProperties?: boolean; properties?: Record<string, unknown>; required?: string[] }
37
+ >;
38
+ for (const name of ['AccountApiKey', 'CreateAccountApiKeyPayload', 'UpdateAccountApiKeyPayload']) {
39
+ expect(components[name].additionalProperties).toBe(false);
40
+ expect(components[name].properties).not.toHaveProperty('value');
41
+ expect(components[name].properties).not.toHaveProperty('project');
42
+ }
43
+ expect(components.ApiKey.required).toContain('project');
44
+ });
45
+ });
46
+
47
+ // AJV enforces emitted JSON Schema; Zod trimming alone cannot reject whitespace at the API boundary.
48
+ describe.each([
49
+ ['CreateAccountApiKeyPayload', CreateAccountApiKeyPayloadSchema],
50
+ ['UpdateAccountApiKeyPayload', UpdateAccountApiKeyPayloadSchema],
51
+ ] as const)('%s name validation', (component, schema) => {
52
+ it.each(['', ' ', '\t\n ', '\u00a0'])('rejects blank name %j in Zod and AJV', (name) => {
53
+ expect(schema.safeParse({ name }).success).toBe(false);
54
+ expect(validateApiRequest(component, { name }).valid).toBe(false);
55
+ });
56
+ it.each(['IdP provisioning', ' IdP provisioning '])('accepts nonblank name %j in both validators', (name) => {
57
+ expect(schema.safeParse({ name }).success).toBe(true);
58
+ expect(validateApiRequest(component, { name }).valid).toBe(true);
59
+ });
60
+ });
@@ -74,7 +74,7 @@ describe('gate 1 — the schema is the single source of truth for the public typ
74
74
  portal_url: 'https://billing.stripe.com/p/session_1',
75
75
  };
76
76
  if (enabled.status === 'enabled') {
77
- assertType<Equals<typeof enabled.portal_url, string>>(true);
77
+ assertType<Equals<typeof enabled.portal_url, string | null>>(true);
78
78
  expect(enabled.portal_url).toBe('https://billing.stripe.com/p/session_1');
79
79
  }
80
80
 
@@ -146,6 +146,7 @@ describe('gate 3 — AJV validates the same canonical objects that are published
146
146
  it('enforces the discriminated union per branch', () => {
147
147
  const validate = compile('StripeBillingStatusResponse');
148
148
  expect(validate({ status: 'enabled', billing_method: 'stripe', portal_url: 'https://x' })).toBe(true);
149
+ expect(validate({ status: 'enabled', billing_method: 'stripe', portal_url: null })).toBe(true);
149
150
  expect(validate({ status: 'disabled', billing_method: null, reason: 'No billing method' })).toBe(true);
150
151
  // The flattened interface allowed this; a real discriminated union does not.
151
152
  expect(validate({ status: 'enabled', billing_method: 'stripe', reason: 'nope' })).toBe(false);
@@ -111,14 +111,7 @@ export const UpdateAccountPayloadSchema = z
111
111
  })
112
112
  .meta({ id: 'UpdateAccountPayload' });
113
113
 
114
- /**
115
- * Stripe billing status, modelled as a real discriminated union.
116
- *
117
- * The hand-written `StripeBillingStatusResponse` in `meters.ts` flattens this into one object
118
- * with `portal_url?` and `reason?` both optional, so a generated Java/Go client gets two
119
- * unrelated optionals and no way to know which is populated. The server only ever sets
120
- * `portal_url` when enabled and `reason` when disabled.
121
- */
114
+ /** Stripe billing status. Account API keys receive status without a billing portal session. */
122
115
  export const StripeBillingEnabledSchema = z
123
116
  .object({
124
117
  status: z.literal('enabled'),
@@ -128,7 +121,10 @@ export const StripeBillingEnabledSchema = z
128
121
  * would describe states the server cannot produce.
129
122
  */
130
123
  billing_method: z.literal(BillingMethod.stripe),
131
- portal_url: z.string(),
124
+ portal_url: z
125
+ .string()
126
+ .nullable()
127
+ .meta({ description: 'Interactive billing portal URL; null for account API keys.' }),
132
128
  })
133
129
  .meta({ id: 'StripeBillingEnabled' });
134
130
 
@@ -2,6 +2,25 @@ import { describe, expect, it } from 'vitest';
2
2
  import { type JsonObject, SchemaAdapterError, toOpenApiComponents } from './adapter.js';
3
3
 
4
4
  describe('reference integrity', () => {
5
+ it('uses the definition when a named root references its own $defs entry', () => {
6
+ const definition = {
7
+ type: 'object',
8
+ properties: { child: { $ref: '#/$defs/Account' } },
9
+ };
10
+ expect(toOpenApiComponents({ Account: { $ref: '#/$defs/Account', $defs: { Account: definition } } })).toEqual({
11
+ Account: { type: 'object', properties: { child: { $ref: '#/components/schemas/Account' } } },
12
+ });
13
+ });
14
+
15
+ it('still rejects conflicting definitions behind a named root reference', () => {
16
+ expect(() =>
17
+ toOpenApiComponents({
18
+ Holder: { type: 'object', $defs: { Account: { type: 'string' } } },
19
+ Account: { $ref: '#/$defs/Account', $defs: { Account: { type: 'number' } } },
20
+ }),
21
+ ).toThrow(SchemaAdapterError);
22
+ });
23
+
5
24
  it('rejects a reference to a component that does not exist', () => {
6
25
  // A dangling pointer produces a spec that validates structurally but breaks every
7
26
  // generated client at deserialization time.
@@ -316,7 +316,14 @@ export function toOpenApiComponents(
316
316
  throw new SchemaAdapterError(`Component '${name}' is not an object schema.`);
317
317
  }
318
318
  ctx.rootName = name;
319
- register(name, walk(structuredClone(schema), ctx, true) as JsonObject, ctx);
319
+ const adapted = walk(structuredClone(schema), ctx, true) as JsonObject;
320
+ // Zod 4.5 emits named roots as a bare reference to their own $defs entry.
321
+ // walk already registered that definition; registering the wrapper would replace it
322
+ // with a self-reference and incorrectly report a conflicting component shape.
323
+ if (adapted[REF] === `${COMPONENT_PREFIX}${name}` && Object.keys(adapted).length === 1 && ctx.seen.has(name)) {
324
+ continue;
325
+ }
326
+ register(name, adapted, ctx);
320
327
  }
321
328
 
322
329
  const strict = options.strictComponents;
@@ -139,6 +139,12 @@ export const RegisterPendingAskRequestSchema = z
139
139
  timeoutHours: z.number().optional(),
140
140
  userChannels: z.array(UserChannelSchema),
141
141
  taskFields: z.array(TaskFieldSchema).optional(),
142
+ /**
143
+ * Id of the REQUEST_INPUT message carrying this ask (the `ask_user` tool-use id), so a
144
+ * notified client can open the exact prompt. Optional: workers built before this field
145
+ * still validate against the component.
146
+ */
147
+ requestId: z.string().optional(),
142
148
  })
143
149
  .meta({ id: 'RegisterPendingAskRequest' });
144
150
 
@@ -0,0 +1,42 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { validateApiRequest, validateApiResponse } from './registry.js';
3
+
4
+ describe('agent history delta wire contract', () => {
5
+ it('accepts optional cursors and bounds their size', () => {
6
+ expect(validateApiRequest('AgentRunDetailsQuery', { include_history: true, from: 'opaque' }).valid).toBe(true);
7
+ expect(validateApiRequest('AgentRunDetailsQuery', { from: {} }).valid).toBe(false);
8
+ expect(validateApiRequest('AgentRunDetailsQuery', { from: 'x'.repeat(12001) }).valid).toBe(false);
9
+ });
10
+ it('validates legacy, snapshot, and delta responses through the registered contract', () => {
11
+ for (const mode of [undefined, 'snapshot', 'delta']) {
12
+ expect(
13
+ validateApiResponse('WorkflowRunWithDetails', {
14
+ started_at: null,
15
+ closed_at: null,
16
+ history: {
17
+ type: 'agent',
18
+ ...(mode ? { mode, next_from: 'opaque' } : {}),
19
+ agentTasks: [
20
+ {
21
+ history_id: 'run:activity:7',
22
+ taskType: 'tool_call',
23
+ toolName: 'test',
24
+ status: 'running',
25
+ scheduled_at: null,
26
+ started_at: null,
27
+ completed_at: null,
28
+ },
29
+ ],
30
+ },
31
+ }).valid,
32
+ ).toBe(true);
33
+ }
34
+ expect(
35
+ validateApiResponse('WorkflowRunWithDetails', {
36
+ started_at: null,
37
+ closed_at: null,
38
+ history: { type: 'agent', mode: 'append', agentTasks: [] },
39
+ }).valid,
40
+ ).toBe(false);
41
+ });
42
+ });
@@ -0,0 +1,240 @@
1
+ import { describe, expect, expectTypeOf, it } from 'vitest';
2
+ import type {
3
+ AgentRunEvaluation,
4
+ AgentRunEvaluationRollup,
5
+ AgentRunFeedbackEntry,
6
+ AgentRunFeedbackPayload,
7
+ AgentRunFeedbackResponse,
8
+ } from '../store/agent-run.js';
9
+ import { AgentEventType, type TurnEvaluationEvent } from '../workflow-analytics.js';
10
+ import type {
11
+ AgentRunEvaluationRollupSchema,
12
+ AgentRunEvaluationSchema,
13
+ AgentRunFeedbackEntrySchema,
14
+ AgentRunFeedbackPayloadSchema,
15
+ AgentRunFeedbackResponseSchema,
16
+ } from './agent-runs.js';
17
+ import { validateApiRequest, validateApiResponse } from './registry.js';
18
+
19
+ const turnEvaluation: TurnEvaluationEvent = {
20
+ eventType: AgentEventType.TurnEvaluation,
21
+ eventId: 'run-1:main:1:answer',
22
+ timestamp: '2026-09-11T10:00:00.000Z',
23
+ runId: 'wf-run-1',
24
+ agentRunId: 'run-1',
25
+ model: 'claude',
26
+ environmentId: 'env-1',
27
+ environmentType: 'vertexai',
28
+ interactionId: 'sys:StudioAssistant',
29
+ schemaVersion: 1,
30
+ detectorVersion: 1,
31
+ workstreamId: 'main',
32
+ turnSeq: 1,
33
+ terminalType: 'answer',
34
+ startedAt: '2026-09-11T09:59:00.000Z',
35
+ endedAt: '2026-09-11T10:00:00.000Z',
36
+ durationMs: 60_000,
37
+ activeMs: 50_000,
38
+ askUserWaitMs: 0,
39
+ approvalWaitMs: 10_000,
40
+ toolCalls: 3,
41
+ errorToolResults: 1,
42
+ maxFailStreak: 1,
43
+ identicalRetryCount: 0,
44
+ unrecoveredTools: [],
45
+ errorClasses: { schema: 1, platform: 0, config: 0, environment: 0, other: 0 },
46
+ gatherCalls: 2,
47
+ gatherRatio: 2 / 3,
48
+ mutationAttempted: true,
49
+ mutationSucceeded: true,
50
+ rereadCount: 0,
51
+ overheadCalls: 0,
52
+ skillsLoaded: 0,
53
+ interruptedToolCalls: 0,
54
+ llmCalls: 4,
55
+ promptTokens: 1000,
56
+ completionTokens: 200,
57
+ cachedTokens: 800,
58
+ retryCompletionTokens: 50,
59
+ approvalsRequested: 1,
60
+ approvalsDenied: 0,
61
+ stopRequests: 0,
62
+ stallCorrectives: 0,
63
+ stallTrips: 0,
64
+ followupAfterAnswer: false,
65
+ severity: 'none',
66
+ flags: [],
67
+ tools: [
68
+ {
69
+ seq: 1,
70
+ toolUseId: 'tu-1',
71
+ name: 'create_view',
72
+ approvalClass: 'side_effecting',
73
+ ok: false,
74
+ errorClass: 'schema',
75
+ sig: 'abcd1234',
76
+ durationMs: 20,
77
+ },
78
+ ],
79
+ toolsTruncated: 0,
80
+ };
81
+
82
+ describe('agent run evaluation API contracts', () => {
83
+ it('derives the public types from the runtime schemas', () => {
84
+ expectTypeOf<AgentRunFeedbackPayload>().toEqualTypeOf<
85
+ import('zod').z.infer<typeof AgentRunFeedbackPayloadSchema>
86
+ >();
87
+ expectTypeOf<AgentRunFeedbackResponse>().toEqualTypeOf<
88
+ import('zod').z.infer<typeof AgentRunFeedbackResponseSchema>
89
+ >();
90
+ expectTypeOf<AgentRunFeedbackEntry>().toEqualTypeOf<
91
+ import('zod').z.infer<typeof AgentRunFeedbackEntrySchema>
92
+ >();
93
+ expectTypeOf<AgentRunEvaluationRollup>().toEqualTypeOf<
94
+ import('zod').z.infer<typeof AgentRunEvaluationRollupSchema>
95
+ >();
96
+ expectTypeOf<AgentRunEvaluation>().toEqualTypeOf<import('zod').z.infer<typeof AgentRunEvaluationSchema>>();
97
+ });
98
+
99
+ it('accepts a turn_evaluation event through the ingest payload', () => {
100
+ expect(validateApiRequest('IngestAgentEventsPayload', { events: [turnEvaluation] }).valid).toBe(true);
101
+ });
102
+
103
+ it('accepts a turn_evaluation event from a producer that predates the stall counters', () => {
104
+ const { stallCorrectives: _c, stallTrips: _t, ...legacy } = turnEvaluation;
105
+ expect(validateApiRequest('IngestAgentEventsPayload', { events: [legacy] }).valid).toBe(true);
106
+ });
107
+
108
+ it('rejects a turn_evaluation event with an undeclared field', () => {
109
+ const events = [{ ...turnEvaluation, verdict: 'success' }];
110
+ expect(validateApiRequest('IngestAgentEventsPayload', { events }).valid).toBe(false);
111
+ });
112
+
113
+ it('accepts a feedback event and a judgement event', () => {
114
+ const base = {
115
+ timestamp: turnEvaluation.timestamp,
116
+ runId: 'wf-run-1',
117
+ agentRunId: 'run-1',
118
+ model: '',
119
+ environmentId: '',
120
+ environmentType: '',
121
+ interactionId: 'sys:StudioAssistant',
122
+ deployment: { env: 'staging', group: 'staging', version: 'abc' },
123
+ };
124
+ const events = [
125
+ {
126
+ ...base,
127
+ eventType: 'feedback',
128
+ feedbackId: 'f1',
129
+ rating: 'down',
130
+ hasComment: false,
131
+ messageScoped: false,
132
+ replaced: false,
133
+ },
134
+ {
135
+ ...base,
136
+ eventType: 'turn_judgement',
137
+ evaluationRev: 2,
138
+ workstreamId: 'main',
139
+ turnSeq: 1,
140
+ gate: 'signal',
141
+ sampleRate: 0,
142
+ selectedProbability: 1,
143
+ outcome: 'judged',
144
+ verdict: 'failure',
145
+ score: 0.2,
146
+ promptVersion: 'v1',
147
+ },
148
+ {
149
+ ...base,
150
+ eventType: 'stall_breaker',
151
+ action: 'trip',
152
+ toolNames: ['fetch_document'],
153
+ repeatCount: 4,
154
+ stallMeasure: 4,
155
+ allErrored: false,
156
+ iteration: 7,
157
+ interactive: true,
158
+ workstreamId: 'main',
159
+ },
160
+ ];
161
+ expect(validateApiRequest('IngestAgentEventsPayload', { events }).valid).toBe(true);
162
+ });
163
+
164
+ it('requires feedback_id and rejects the retired episode_seq field', () => {
165
+ expect(validateApiRequest('AgentRunFeedbackPayload', { rating: 'up' }).valid).toBe(false);
166
+ expect(validateApiRequest('AgentRunFeedbackPayload', { feedback_id: 'f1', rating: 'up' }).valid).toBe(true);
167
+ expect(
168
+ validateApiRequest('AgentRunFeedbackPayload', { feedback_id: 'f1', rating: 'up', episode_seq: 1 }).valid,
169
+ ).toBe(false);
170
+ });
171
+
172
+ it('keeps the workflow rollup free of server-owned fields', () => {
173
+ const rollup: AgentRunEvaluationRollup = {
174
+ seq: 3,
175
+ detector_version: 1,
176
+ turns: 2,
177
+ severity: 'medium',
178
+ flags: ['unrecovered_tool'],
179
+ worst_turn_seq: 2,
180
+ last_terminal_type: 'answer',
181
+ totals: {
182
+ tool_calls: 5,
183
+ error_tool_results: 2,
184
+ unrecovered_tools: ['create_view'],
185
+ llm_calls: 6,
186
+ prompt_tokens: 100,
187
+ completion_tokens: 20,
188
+ cached_tokens: 0,
189
+ retry_completion_tokens: 5,
190
+ active_ms: 1000,
191
+ ask_user_wait_ms: 0,
192
+ approval_wait_ms: 0,
193
+ followups: 0,
194
+ approvals_requested: 0,
195
+ approvals_denied: 0,
196
+ stop_requests: 0,
197
+ stall_trips: 0,
198
+ },
199
+ updated_at: turnEvaluation.timestamp,
200
+ };
201
+ expect(validateApiRequest('UpdateAgentRunStatusPayload', { evaluation_rollup: rollup }).valid).toBe(true);
202
+ // Rollups stored or sent before the stall counter existed keep validating.
203
+ const { stall_trips: _s, ...legacyTotals } = rollup.totals;
204
+ expect(
205
+ validateApiRequest('UpdateAgentRunStatusPayload', {
206
+ evaluation_rollup: { ...rollup, totals: legacyTotals },
207
+ }).valid,
208
+ ).toBe(true);
209
+ expect(
210
+ validateApiResponse('AgentRunEvaluation', {
211
+ rev: 1,
212
+ rollup: { ...rollup, totals: legacyTotals },
213
+ severity: 'medium',
214
+ flags: ['unrecovered_tool'],
215
+ contradicted: false,
216
+ updated_at: turnEvaluation.timestamp,
217
+ }).valid,
218
+ ).toBe(true);
219
+ expect(
220
+ validateApiRequest('UpdateAgentRunStatusPayload', {
221
+ evaluation_rollup: { ...rollup, feedback_counts: { up: 1, down: 0 } },
222
+ }).valid,
223
+ ).toBe(false);
224
+ });
225
+
226
+ it('validates the published evaluation summary', () => {
227
+ const evaluation: AgentRunEvaluation = {
228
+ rev: 2,
229
+ feedback_counts: { up: 0, down: 1, last_rating: 'down', last_reason_code: 'wrong_result' },
230
+ severity: 'none',
231
+ flags: [],
232
+ contradicted: true,
233
+ contradiction_reasons: ['feedback_down_on_clean_run'],
234
+ deployment_env: 'staging',
235
+ updated_at: turnEvaluation.timestamp,
236
+ };
237
+ expect(validateApiResponse('AgentRunEvaluation', evaluation).valid).toBe(true);
238
+ expect(validateApiRequest('ListAgentRunsQuery', { evaluation_severity: ['unrated', 'high'] }).valid).toBe(true);
239
+ });
240
+ });