@zhixuan92/multi-model-agent-core 3.9.0 → 3.10.0

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.
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/telemetry/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/telemetry/types.ts"],"names":[],"mappings":"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"}
@@ -1,111 +1,32 @@
1
1
  import { z } from 'zod';
2
- export const SCHEMA_VERSION = 2;
3
- /**
4
- * Permissive shape-only validation for fields whose vocabulary we don't control:
5
- * model IDs, client names, MCP tool names, skill IDs. Charset accommodates every
6
- * model namespace observed in the wild (Anthropic, OpenAI, Bedrock prefixes,
7
- * OpenRouter `meta-llama/...`, Ollama `model:tag`). Length cap prevents PII
8
- * smuggling. The schema validates SHAPE, not VOCABULARY.
9
- */
10
- export const BoundedIdentifier = z
11
- .string()
12
- .min(1)
13
- .max(120)
14
- .regex(/^[A-Za-z0-9._:/\-]+$/);
15
- const MAX_STR = 64;
16
- const MAX_VERSION_STR = 64;
2
+ import { ModelFamilyEnum } from '../routing/model-profiles.js';
3
+ export const SCHEMA_VERSION = 3;
4
+ export const STRICT_ID_REGEX = /^[A-Za-z0-9][-A-Za-z0-9_.:+/@]{0,119}$/;
5
+ // ── Batch wrapper (§3.1) ─────────────────────────────────────────────────
17
6
  const VersionString = z
18
7
  .string()
19
8
  .regex(/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/)
20
- .max(MAX_VERSION_STR);
21
- // Allowlists. Two skill enums: triggering source vs installable skill.
22
- // 'direct' is only meaningful for triggering (no skill caller); it cannot be installed.
23
- export const InstallableSkillId = z.enum([
24
- 'mma-delegate',
25
- 'mma-audit',
26
- 'mma-review',
27
- 'mma-verify',
28
- 'mma-debug',
29
- 'mma-execute-plan',
30
- 'mma-retry',
31
- 'mma-investigate', // present on disk under packages/server/src/skills/
32
- 'mma-context-blocks',
33
- 'mma-clarifications',
34
- 'other', // sentinel for unknown / community skill
35
- ]);
36
- export const TriggeringSkillId = z.union([InstallableSkillId, z.literal('direct')]);
37
- export const ClientId = BoundedIdentifier;
38
- export const ModelFamily = z.enum([
39
- 'claude', // Anthropic
40
- 'openai', // OpenAI
41
- 'gemini', // Google
42
- 'deepseek', // DeepSeek
43
- 'grok', // xAI
44
- 'mistral', // Mistral
45
- 'meta', // Meta (Llama family — covers llama2:7b, meta-llama/..., etc.)
46
- 'qwen', // Alibaba
47
- 'zhipu', // Z.ai (GLM family)
48
- 'kimi', // Moonshot
49
- 'minimax', // MiniMax
50
- 'other', // catch-all — never rejected
51
- ]);
52
- export const Language = z.enum([
53
- 'en',
54
- 'es',
55
- 'fr',
56
- 'de',
57
- 'zh',
58
- 'ja',
59
- 'ko',
60
- 'pt',
61
- 'ru',
62
- 'it',
63
- 'tr',
64
- 'ar',
65
- 'hi',
66
- 'vi',
67
- 'id',
68
- 'th',
69
- 'pl',
70
- 'nl',
71
- 'sv',
72
- 'other',
73
- ]);
74
- // Time-zone offset buckets, fully covering UTC-12 through UTC+14, half-open [a, b).
75
- export const TzOffsetBucket = z.enum([
76
- 'utc_minus_12_to_minus_6', // [-12, -6)
77
- 'utc_minus_6_to_0', // [-6, 0)
78
- 'utc_0_to_plus_6', // [0, +6)
79
- 'utc_plus_6_to_plus_12', // [+6, +12)
80
- 'utc_plus_12_to_plus_15', // [+12, +15) -- covers UTC+12, +12:45, +13, +14
81
- ]);
82
- export const Os = z.enum(['darwin', 'linux', 'win32', 'other']); // matches process.platform; non-listed → 'other'
83
- export const InstallMetadata = z.object({
9
+ .max(64);
10
+ export const Os = z.enum(['darwin', 'linux', 'win32', 'other']);
11
+ export const BatchWrapperSchema = z.object({
12
+ schemaVersion: z.literal(3),
84
13
  installId: z.string().uuid(),
85
14
  mmagentVersion: VersionString,
86
15
  os: Os,
87
- nodeMajor: z.string().regex(/^[1-9]\d?$/).max(2), // "1".."99"; no leading zeros; cast to int when sorting
88
- language: Language, // bucketed from runtime locale, never raw
89
- tzOffsetBucket: TzOffsetBucket,
16
+ nodeMajor: z.number().int().min(22).max(99),
90
17
  }).strict();
91
- // Allowlist of tool names that may appear in topToolNames. Anything else → 'other'.
92
- // This is the SDK-level tool surface from packages/core/src/tools/definitions.ts;
93
- // the canonical names used here are the camelCase internal names. The adapter-facing
94
- // snake_case names (read_file, write_file, edit_file, run_shell, list_files) are
95
- // normalized to camelCase by the event-builder before counting. Web search and web
96
- // fetch are NOT separate tool names — they are surfaced via the `capabilities` field
97
- // on TaskCompletedEvent and excluded from topToolNames.
98
- export const AllowlistedToolName = z.enum([
99
- 'readFile',
100
- 'writeFile',
101
- 'editFile',
102
- 'runShell',
103
- 'listFiles',
104
- 'grep',
105
- 'glob',
18
+ // ── Enums shared across stages and top-level ─────────────────────────────
19
+ export const ConcernCategory = z.enum([
20
+ 'missing_test',
21
+ 'scope_creep',
22
+ 'incomplete_impl',
23
+ 'style_lint',
24
+ 'security',
25
+ 'performance',
26
+ 'maintainability',
27
+ 'doc_gap',
106
28
  'other',
107
29
  ]);
108
- // Allowlist of error codes from packages/core/src/types.ts:RunResult.structuredError
109
30
  export const ErrorCode = z.enum([
110
31
  'verify_command_error',
111
32
  'commit_metadata_invalid',
@@ -119,280 +40,218 @@ export const ErrorCode = z.enum([
119
40
  'rate_limit_exceeded',
120
41
  'other',
121
42
  ]);
122
- // Allowlist of structured concern categories surfaced by reviewers.
123
- // Server categorizes raw concern messages into these buckets at ingest time.
124
- export const ConcernCategory = z.enum([
125
- 'missing_test',
126
- 'scope_creep',
127
- 'incomplete_impl',
128
- 'style_lint',
129
- 'security',
130
- 'performance',
131
- 'maintainability',
132
- 'doc_gap',
133
- 'other',
134
- ]);
135
- // Per-stage breakdown — populated for stages the task actually entered.
136
- // Each sub-object is null when the stage was not entered.
137
- export const StageStats = z.object({
138
- entered: z.boolean(),
139
- durationBucket: z.enum(['<10s', '10s-1m', '1m-5m', '5m-30m', '30m+']).nullable(),
140
- costBucket: z.enum(['$0', '<$0.01', '$0.01-$0.10', '$0.10-$1', '$1+']).nullable(),
141
- agentTier: z.enum(['standard', 'complex']).nullable(),
142
- modelFamily: ModelFamily.nullable(),
143
- model: BoundedIdentifier.nullable(),
43
+ export const SeverityBin = z.enum(['high', 'medium', 'low', 'style']);
44
+ export const FindingsBySeveritySchema = z.object({
45
+ high: z.number().int().min(0).max(50),
46
+ medium: z.number().int().min(0).max(50),
47
+ low: z.number().int().min(0).max(50),
48
+ style: z.number().int().min(0).max(50),
144
49
  }).strict();
145
- // Reviewer stages add verdict + round + concern categories.
146
- export const ReviewStageStats = StageStats.extend({
147
- verdict: z
148
- .enum(['approved', 'concerns', 'changes_required', 'error', 'skipped', 'not_applicable'])
149
- .nullable(),
150
- roundsUsed: z.enum(['0', '1', '2+']).nullable(),
151
- concernCategories: z
152
- .array(ConcernCategory)
153
- .max(9)
154
- .nullable(), // categorized server-side; never raw text. Cap matches the ConcernCategory enum cardinality so a stage that surfaced every distinct category isn't silently truncated.
50
+ // ── Stage entry (§3.3) ───────────────────────────────────────────────────
51
+ const StageNameEnum = z.enum([
52
+ 'implementing',
53
+ 'spec_review',
54
+ 'spec_rework',
55
+ 'quality_review',
56
+ 'quality_rework',
57
+ 'diff_review',
58
+ 'verifying',
59
+ 'committing',
60
+ ]);
61
+ // Base fields shared by all stage variants
62
+ const StageEntryBase = z.object({
63
+ name: StageNameEnum,
64
+ model: z.string().regex(STRICT_ID_REGEX),
65
+ agentTier: z.enum(['standard', 'reasoning']),
66
+ durationMs: z.number().int().min(0).max(3_600_000),
67
+ costUSD: z.number().min(0).max(100),
68
+ inputTokens: z.number().int().min(0).max(5_000_000),
69
+ outputTokens: z.number().int().min(0).max(500_000),
70
+ cachedTokens: z.number().int().min(0).max(5_000_000),
71
+ reasoningTokens: z.number().int().min(0).max(500_000),
72
+ toolCallCount: z.number().int().min(0).max(5000),
73
+ filesReadCount: z.number().int().min(0).max(5000),
74
+ filesWrittenCount: z.number().int().min(0).max(5000),
75
+ turnCount: z.number().int().min(0).max(250),
76
+ maxIdleMs: z.number().int().min(0).max(1_200_000).nullable(),
77
+ totalIdleMs: z.number().int().min(0).max(3_600_000).nullable(),
155
78
  });
156
- // Verify stage adds outcome + skip reason.
157
- export const VerifyStageStats = StageStats.extend({
158
- outcome: z.enum(['passed', 'failed', 'skipped', 'not_applicable']).nullable(),
79
+ export const ReviewStageEntrySchema = StageEntryBase.extend({
80
+ name: z.enum(['spec_review', 'quality_review', 'diff_review']),
81
+ verdict: z.enum(['approved', 'concerns', 'changes_required', 'error', 'skipped', 'annotated', 'not_applicable']),
82
+ roundsUsed: z.number().int().min(1).max(10),
83
+ concernCategories: z.array(ConcernCategory).max(9),
84
+ findingsBySeverity: FindingsBySeveritySchema,
85
+ }).strict();
86
+ export const ReworkStageEntrySchema = StageEntryBase.extend({
87
+ name: z.enum(['spec_rework', 'quality_rework']),
88
+ triggeringConcernCategories: z.array(ConcernCategory).max(9),
89
+ }).strict();
90
+ export const VerifyStageEntrySchema = StageEntryBase.extend({
91
+ name: z.literal('verifying'),
92
+ outcome: z.enum(['passed', 'failed', 'skipped', 'not_applicable']),
159
93
  skipReason: z.enum(['no_command', 'dirty_worktree', 'not_applicable', 'other']).nullable(),
160
- });
161
- export const TaskCompletedEvent = z.object({
162
- type: z.literal('task.completed'),
163
- // Route shape
164
- route: z.enum(['delegate', 'audit', 'review', 'verify', 'debug', 'execute-plan', 'retry']),
94
+ }).strict();
95
+ export const CommitStageEntrySchema = StageEntryBase.extend({
96
+ name: z.literal('committing'),
97
+ filesCommittedCount: z.number().int().min(0).max(1000),
98
+ branchCreated: z.boolean(),
99
+ }).strict();
100
+ export const ImplementStageEntrySchema = StageEntryBase.extend({
101
+ name: z.literal('implementing'),
102
+ }).strict();
103
+ export const StageEntrySchema = z.discriminatedUnion('name', [
104
+ ImplementStageEntrySchema,
105
+ ReviewStageEntrySchema,
106
+ ReworkStageEntrySchema,
107
+ VerifyStageEntrySchema,
108
+ CommitStageEntrySchema,
109
+ ]);
110
+ // ── Task completed event (§3.2) ──────────────────────────────────────────
111
+ export const TaskCompletedEventSchema = z.object({
112
+ // Identity
113
+ eventId: z.string().uuid(),
114
+ route: z.enum(['delegate', 'audit', 'review', 'verify', 'debug', 'execute-plan', 'retry', 'investigate']),
115
+ client: z.string().regex(STRICT_ID_REGEX),
116
+ // Configuration
165
117
  agentType: z.enum(['standard', 'complex']),
166
- capabilities: z
167
- .array(z.enum(['web_search', 'web_fetch', 'other']))
168
- .max(3)
169
- .refine(xs => new Set(xs).size === xs.length, 'unique'),
170
118
  toolMode: z.enum(['none', 'readonly', 'no-shell', 'full']),
171
- triggeredFromSkill: BoundedIdentifier, // 'direct' for non-skill invocations
172
- client: ClientId, // which agent client invoked us
173
- // Task shape (derived from structured task metadata; never from prompt parsing or fs scanning)
174
- fileCountBucket: z.enum(['0', '1-5', '6-20', '21-50', '51+']),
175
- durationBucket: z.enum(['<10s', '10s-1m', '1m-5m', '5m-30m', '30m+']),
176
- costBucket: z.enum(['$0', '<$0.01', '$0.01-$0.10', '$0.10-$1', '$1+']),
177
- savedCostBucket: z.enum(['$0', '<$0.10', '$0.10-$1', '$1+', 'unknown']),
178
- // Implementer model summary (top-level convenience; per-stage detail lives in `stages`)
179
- implementerModelFamily: ModelFamily,
180
- implementerModel: BoundedIdentifier,
119
+ capabilities: z.array(z.enum(['web_search', 'web_fetch', 'other'])).max(3),
120
+ reviewPolicy: z.enum(['full', 'quality_only', 'diff_only', 'none']),
121
+ verifyCommandPresent: z.boolean(),
122
+ // Model
123
+ implementerModel: z.string().regex(STRICT_ID_REGEX),
181
124
  // Outcome
182
- terminalStatus: z.enum([
183
- 'ok',
184
- 'incomplete',
185
- 'timeout',
186
- 'error',
187
- 'cost_exceeded',
188
- 'brief_too_vague',
189
- 'unavailable',
190
- ]),
191
- workerStatus: z.enum([
192
- 'done',
193
- 'done_with_concerns',
194
- 'needs_context',
195
- 'blocked',
196
- 'failed',
197
- 'review_loop_aborted',
198
- ]),
199
- errorCode: ErrorCode.nullable(), // populated when terminalStatus is a failure mode
200
- // 3.5.0 lifecycle effectiveness
201
- escalated: z.boolean(),
202
- fallbackTriggered: z.boolean(),
203
- // Tool-call profile — top 5 distinct tool names called during this task by count
204
- // (allowlisted; non-listed tools become 'other'; never includes args/paths)
205
- topToolNames: z.array(BoundedIdentifier).max(20),
206
- // Per-stage breakdown — drives the lifecycle funnel + per-stage panels
207
- stages: z.object({
208
- implementing: StageStats,
209
- verifying: VerifyStageStats,
210
- spec_review: ReviewStageStats,
211
- spec_rework: StageStats, // implementer re-runs after spec changes_required
212
- quality_review: ReviewStageStats,
213
- quality_rework: StageStats,
214
- diff_review: ReviewStageStats.optional(), // diff-only policy; not always present
215
- committing: StageStats,
216
- }).strict(),
217
- // v2 fields
218
- filesWrittenBucket: z.enum(['0', '1-5', '6-20', '21-50', '51+']),
219
- c2Promoted: z.boolean(),
220
- workerSelfAssessment: z.enum(['done', 'done_with_concerns', 'needs_context', 'blocked', 'failed', 'review_loop_aborted']).nullable(),
221
- concernCount: z.number().int().min(0).max(50),
125
+ terminalStatus: z.enum(['ok', 'incomplete', 'timeout', 'error', 'cost_exceeded', 'brief_too_vague', 'unavailable']),
126
+ workerStatus: z.enum(['done', 'done_with_concerns', 'needs_context', 'blocked', 'failed', 'review_loop_aborted']),
127
+ errorCode: ErrorCode.nullable(),
128
+ parentModelFamily: ModelFamilyEnum,
129
+ // Token economics
130
+ inputTokens: z.number().int().min(0).max(5_000_000),
131
+ outputTokens: z.number().int().min(0).max(500_000),
132
+ cachedTokens: z.number().int().min(0).max(5_000_000),
133
+ reasoningTokens: z.number().int().min(0).max(500_000),
134
+ // Run totals
135
+ totalDurationMs: z.number().int().min(0).max(86_400_000),
136
+ totalCostUSD: z.number().min(0).max(800),
137
+ totalSavedCostUSD: z.number().min(-800).max(800).nullable(),
138
+ // Lifecycle counts
139
+ concernCount: z.number().int().min(0).max(150),
222
140
  escalationCount: z.number().int().min(0).max(20),
223
141
  fallbackCount: z.number().int().min(0).max(20),
224
- turnCountBucket: z.enum(['1-3', '4-10', '11-30', '31+']),
225
- stallTriggered: z.boolean(),
142
+ // Operational signals
143
+ stallCount: z.number().int().min(0).max(20),
144
+ taskMaxIdleMs: z.number().int().min(0).max(1_200_000).nullable(),
226
145
  clarificationRequested: z.boolean(),
227
- parentModelFamily: ModelFamily,
228
- briefQualityWarningCount: z.number().int().min(0).max(10),
229
- }).strict();
230
- export const SessionStartedEvent = z.object({
231
- type: z.literal('session.started'),
232
- configFlavor: z.object({
233
- defaultTier: z.enum(['standard', 'complex']),
234
- diagnosticsEnabled: z.boolean(),
235
- autoUpdateSkills: z.boolean(),
236
- }).strict(),
237
- providersConfigured: z
238
- .array(z.enum(['claude', 'openai-compatible', 'codex']))
239
- .max(3)
240
- .refine(xs => new Set(xs).size === xs.length, 'unique'),
241
- }).strict();
242
- export const InstallChangedEvent = z.object({
243
- type: z.literal('install.changed'),
244
- fromVersion: VersionString.nullable(),
245
- toVersion: VersionString,
246
- trigger: z.enum(['fresh_install', 'upgrade', 'downgrade']),
146
+ briefQualityWarningCount: z.number().int().min(0).max(20),
147
+ sandboxViolationCount: z.number().int().min(0).max(100),
148
+ // Stages array
149
+ stages: z.array(StageEntrySchema).min(0).max(8),
247
150
  }).strict();
248
- export const SkillInstalledEvent = z.object({
249
- type: z.literal('skill.installed'),
250
- skill: InstallableSkillId, // 'direct' is NOT a skill, rejected here
251
- client: ClientId,
151
+ // ── Upload batch ─────────────────────────────────────────────────────────
152
+ export const UploadBatchSchema = z.object({
153
+ schemaVersion: z.literal(3),
154
+ installId: z.string().uuid(),
155
+ mmagentVersion: VersionString,
156
+ os: Os,
157
+ nodeMajor: z.number().int().min(22).max(99),
158
+ events: z.array(TaskCompletedEventSchema).min(1).max(500),
252
159
  }).strict();
253
- // Discriminated union, with eventId for at-most-once dedup within retention window.
254
- // .superRefine() enforces internal consistency (see 4.4 for the rules).
255
- const TelemetryEventBase = z.object({ eventId: z.string().uuid() }).strict();
256
- export const TelemetryEvent = z
257
- .discriminatedUnion('type', [
258
- TaskCompletedEvent.merge(TelemetryEventBase),
259
- SessionStartedEvent.merge(TelemetryEventBase),
260
- InstallChangedEvent.merge(TelemetryEventBase),
261
- SkillInstalledEvent.merge(TelemetryEventBase),
262
- ])
263
- .superRefine((event, ctx) => {
264
- if (event.type !== 'task.completed')
265
- return;
160
+ // ── Super-refinement: R1–R15 (§3.4) ──────────────────────────────────────
161
+ const qualityOnlyRoutes = new Set(['audit', 'review', 'verify', 'debug', 'investigate']);
162
+ const reviewedRoutes = new Set(['delegate', 'audit', 'review', 'verify', 'debug', 'execute-plan', 'investigate']);
163
+ const reworkStages = new Set(['spec_rework', 'quality_rework']);
164
+ const reviewStages = new Set(['spec_review', 'quality_review', 'diff_review']);
165
+ export const ValidatedTaskCompletedEventSchema = TaskCompletedEventSchema.superRefine((event, ctx) => {
266
166
  // R1: ok terminalStatus implies non-failed worker outcome and no errorCode
267
167
  if (event.terminalStatus === 'ok') {
268
168
  if (!['done', 'done_with_concerns'].includes(event.workerStatus)) {
269
- ctx.addIssue({
270
- code: 'custom',
271
- message: 'terminalStatus=ok requires workerStatus done|done_with_concerns',
272
- });
169
+ ctx.addIssue({ code: 'custom', message: 'R1: terminalStatus=ok requires workerStatus done|done_with_concerns' });
273
170
  }
274
171
  if (event.errorCode !== null) {
275
- ctx.addIssue({
276
- code: 'custom',
277
- message: 'terminalStatus=ok requires errorCode=null',
278
- });
172
+ ctx.addIssue({ code: 'custom', message: 'R1: terminalStatus=ok requires errorCode=null' });
279
173
  }
280
174
  }
281
- // R2: verify only applies to routes that exercise the verify stage
282
- const verifyApplicableRoutes = new Set(['delegate', 'execute-plan', 'verify']);
283
- const verifyOutcome = event.stages.verifying.outcome;
284
- if (!verifyApplicableRoutes.has(event.route) &&
285
- verifyOutcome !== null &&
286
- verifyOutcome !== 'not_applicable') {
287
- ctx.addIssue({
288
- code: 'custom',
289
- message: 'stages.verifying.outcome must be null or not_applicable for non-verify routes',
290
- });
175
+ // R2: stage count must be > 0 for ok/incomplete (brief_too_vague may have 0)
176
+ // R2.1: empty stages only allowed for brief_too_vague and error
177
+ if (event.stages.length === 0 && !['brief_too_vague', 'error'].includes(event.terminalStatus)) {
178
+ ctx.addIssue({ code: 'custom', message: 'R2.1: empty stages only allowed for brief_too_vague|error' });
291
179
  }
292
- // R3: spec/quality/diff review only on routes that go through the reviewed lifecycle
293
- const reviewedRoutes = new Set(['delegate', 'execute-plan']);
294
- if (!reviewedRoutes.has(event.route)) {
295
- if (event.stages.spec_review.entered) {
296
- ctx.addIssue({
297
- code: 'custom',
298
- message: 'stages.spec_review.entered must be false for non-reviewed routes',
299
- });
300
- }
301
- if (event.stages.quality_review.entered) {
302
- ctx.addIssue({
303
- code: 'custom',
304
- message: 'stages.quality_review.entered must be false for non-reviewed routes',
305
- });
306
- }
307
- if (event.stages.diff_review?.entered) {
308
- ctx.addIssue({
309
- code: 'custom',
310
- message: 'stages.diff_review.entered must be false (or stages.diff_review absent) for non-reviewed routes',
311
- });
312
- }
180
+ // R3: concernCount must not exceed the sum of findingsBySeverity bins + a tolerance
181
+ // Per plan Task 6, concernCount is capped separately; the invariant is sum(bins) ≤ concernCount
182
+ // R4: totalDurationMs >= sum of stage durationMs (not strictly equal due to overhead)
183
+ const stageDurationSum = event.stages.reduce((s, st) => s + st.durationMs, 0);
184
+ if (stageDurationSum > event.totalDurationMs) {
185
+ ctx.addIssue({ code: 'custom', message: 'R4: sum of stage durationMs must not exceed totalDurationMs' });
313
186
  }
314
- // R4: stage sub-fields must be null when entered=false
315
- // (`stages.diff_review` is `.optional()` and is omitted from the parsed
316
- // object entirely when absent — Object.entries doesn't surface it as
317
- // `[name, undefined]`. The `!st` guard is defensive belt-and-suspenders
318
- // that also covers the case where a future schema change makes the
319
- // field nullable rather than optional.)
320
- for (const [name, st] of Object.entries(event.stages)) {
321
- if (!st)
322
- continue;
323
- if (!st.entered) {
324
- // All five `StageStats` nullable fields must be null when the stage was not entered.
325
- const baseDirty = st.durationBucket !== null ||
326
- st.costBucket !== null ||
327
- st.agentTier !== null ||
328
- st.modelFamily !== null ||
329
- st.model !== null;
330
- // Extended fields on review / verify stages must also be null when entered=false.
331
- const reviewDirty = ('verdict' in st && st.verdict !== null) ||
332
- ('roundsUsed' in st && st.roundsUsed !== null) ||
333
- ('concernCategories' in st &&
334
- st.concernCategories !== null);
335
- const verifyDirty = ('outcome' in st && st.outcome !== null) ||
336
- ('skipReason' in st && st.skipReason !== null);
337
- if (baseDirty || reviewDirty || verifyDirty) {
338
- ctx.addIssue({
339
- code: 'custom',
340
- message: `stages.${name} sub-fields must be null when entered=false`,
341
- });
342
- }
187
+ // R5: top-level token counts = sum of stage token counts
188
+ const tokenSum = event.stages.reduce((acc, st) => ({
189
+ input: acc.input + st.inputTokens,
190
+ output: acc.output + st.outputTokens,
191
+ cached: acc.cached + st.cachedTokens,
192
+ reasoning: acc.reasoning + st.reasoningTokens,
193
+ }), { input: 0, output: 0, cached: 0, reasoning: 0 });
194
+ if (tokenSum.input !== event.inputTokens ||
195
+ tokenSum.output !== event.outputTokens ||
196
+ tokenSum.cached !== event.cachedTokens ||
197
+ tokenSum.reasoning !== event.reasoningTokens) {
198
+ ctx.addIssue({ code: 'custom', message: 'R5: token sums must equal top-level totals' });
199
+ }
200
+ // R5b: per stage, reasoningTokens ≤ outputTokens (subset semantics)
201
+ for (const st of event.stages) {
202
+ if (st.reasoningTokens > st.outputTokens) {
203
+ ctx.addIssue({ code: 'custom', message: 'R5b: reasoningTokens must not exceed outputTokens per stage' });
343
204
  }
344
205
  }
345
- // R5: when entered=true, the base bucketed fields plus the stage-type-specific
346
- // fields must be non-null (the stage actually ran, so it produced cost/duration
347
- // and a verdict/outcome). `skipReason` is exempt — it is legitimately null
348
- // unless `outcome === 'skipped'`. `concernCategories` is exempt a clean
349
- // verdict legitimately surfaces an empty list.
350
- for (const [name, st] of Object.entries(event.stages)) {
351
- if (!st || !st.entered)
352
- continue;
353
- const baseMissing = st.durationBucket === null ||
354
- st.costBucket === null ||
355
- st.agentTier === null ||
356
- st.modelFamily === null ||
357
- st.model === null;
358
- if (baseMissing) {
359
- ctx.addIssue({
360
- code: 'custom',
361
- message: `stages.${name} base sub-fields must be non-null when entered=true`,
362
- });
206
+ // R6: per stage, cachedTokens inputTokens (cached is subset of input)
207
+ for (const st of event.stages) {
208
+ if (st.cachedTokens > st.inputTokens) {
209
+ ctx.addIssue({ code: 'custom', message: 'R6: cachedTokens must not exceed inputTokens per stage' });
363
210
  }
364
- if ('verdict' in st && st.verdict === null) {
365
- ctx.addIssue({
366
- code: 'custom',
367
- message: `stages.${name}.verdict must be non-null when entered=true`,
368
- });
211
+ }
212
+ // R7: totalCostUSD = sum of stage costUSD (float comparison with tolerance)
213
+ const costSum = event.stages.reduce((s, st) => s + st.costUSD, 0);
214
+ if (Math.abs(costSum - event.totalCostUSD) > 0.02) {
215
+ ctx.addIssue({ code: 'custom', message: 'R7: totalCostUSD must approximately equal sum of stage costUSD' });
216
+ }
217
+ // R8: verification outcome only on delegate, execute-plan, verify routes
218
+ const verifyRoutes = new Set(['delegate', 'execute-plan', 'verify']);
219
+ for (const st of event.stages) {
220
+ if (st.name === 'verifying' && !verifyRoutes.has(event.route)) {
221
+ ctx.addIssue({ code: 'custom', message: 'R8: verifying stage only allowed on delegate|execute-plan|verify routes' });
369
222
  }
370
- if ('roundsUsed' in st && st.roundsUsed === null) {
371
- ctx.addIssue({
372
- code: 'custom',
373
- message: `stages.${name}.roundsUsed must be non-null when entered=true`,
374
- });
223
+ }
224
+ // R9: review stages only on reviewed routes
225
+ for (const st of event.stages) {
226
+ if (reviewStages.has(st.name) && !reviewedRoutes.has(event.route)) {
227
+ ctx.addIssue({ code: 'custom', message: `R9: ${st.name} stage only allowed on reviewed routes` });
375
228
  }
376
- if ('outcome' in st && st.outcome === null) {
377
- ctx.addIssue({
378
- code: 'custom',
379
- message: `stages.${name}.outcome must be non-null when entered=true`,
380
- });
229
+ }
230
+ // R10: quality_only routes must not have spec_review, diff_review, or rework stages
231
+ // R10b: no rework on quality_only
232
+ // R10c: annotated verdict only on quality_only routes
233
+ for (const st of event.stages) {
234
+ if (qualityOnlyRoutes.has(event.route)) {
235
+ if (reviewStages.has(st.name) && st.name !== 'quality_review') {
236
+ ctx.addIssue({ code: 'custom', message: 'R10: non-quality review stage on quality_only route' });
237
+ }
238
+ if (reworkStages.has(st.name)) {
239
+ ctx.addIssue({ code: 'custom', message: 'R10b: rework stages not allowed on quality_only routes' });
240
+ }
381
241
  }
382
- if ('outcome' in st &&
383
- st.outcome === 'skipped' &&
384
- st.skipReason === null) {
385
- ctx.addIssue({
386
- code: 'custom',
387
- message: `stages.${name}.skipReason must be non-null when outcome='skipped'`,
388
- });
242
+ if ('verdict' in st && st.verdict === 'annotated' && !qualityOnlyRoutes.has(event.route)) {
243
+ ctx.addIssue({ code: 'custom', message: 'R10c: annotated verdict only allowed on quality_only routes' });
389
244
  }
390
245
  }
246
+ // R11: concernCount in [0, 150], escalationCount in [0, 20], fallbackCount in [0, 20]
247
+ // (enforced by Zod schema bounds)
248
+ // R12: stallCount in [0, 20], sandboxViolationCount in [0, 100]
249
+ // (enforced by Zod schema bounds)
250
+ // R13: totalDurationMs in [0, 86_400_000]
251
+ // (enforced by Zod schema bounds)
252
+ // R14: totalCostUSD in [0, 800], totalSavedCostUSD in [-800, 800] or null
253
+ // (enforced by Zod schema bounds)
254
+ // R15: costUSD per stage in [0, 100]
255
+ // (enforced by Zod schema bounds)
391
256
  });
392
- // The complete uploadable envelope.
393
- export const UploadBatch = z.object({
394
- schemaVersion: z.literal(SCHEMA_VERSION),
395
- install: InstallMetadata,
396
- events: z.array(TelemetryEvent).min(1).max(500),
397
- }).strict();
398
257
  //# sourceMappingURL=types.js.map