@cleocode/contracts 2026.5.121 → 2026.5.122
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/dist/__tests__/satisfies-atom.test.d.ts +24 -0
- package/dist/__tests__/satisfies-atom.test.d.ts.map +1 -0
- package/dist/__tests__/satisfies-atom.test.js +302 -0
- package/dist/__tests__/satisfies-atom.test.js.map +1 -0
- package/dist/__tests__/validator-types.test.d.ts +13 -0
- package/dist/__tests__/validator-types.test.d.ts.map +1 -0
- package/dist/__tests__/validator-types.test.js +293 -0
- package/dist/__tests__/validator-types.test.js.map +1 -0
- package/dist/config.d.ts +56 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/data-accessor.d.ts +117 -0
- package/dist/data-accessor.d.ts.map +1 -1
- package/dist/data-accessor.js.map +1 -1
- package/dist/evidence-atom-schema.d.ts +91 -0
- package/dist/evidence-atom-schema.d.ts.map +1 -1
- package/dist/evidence-atom-schema.js +162 -1
- package/dist/evidence-atom-schema.js.map +1 -1
- package/dist/exit-codes.d.ts +41 -1
- package/dist/exit-codes.d.ts.map +1 -1
- package/dist/exit-codes.js +40 -0
- package/dist/exit-codes.js.map +1 -1
- package/dist/index.d.ts +7 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/operations/tasks.d.ts +49 -0
- package/dist/operations/tasks.d.ts.map +1 -1
- package/dist/operations/tasks.js.map +1 -1
- package/dist/task.d.ts +38 -0
- package/dist/task.d.ts.map +1 -1
- package/dist/validator/index.d.ts +375 -0
- package/dist/validator/index.d.ts.map +1 -0
- package/dist/validator/index.js +162 -0
- package/dist/validator/index.js.map +1 -0
- package/package.json +2 -2
- package/src/__tests__/satisfies-atom.test.ts +363 -0
- package/src/__tests__/validator-types.test.ts +359 -0
- package/src/config.ts +58 -0
- package/src/data-accessor.ts +120 -0
- package/src/evidence-atom-schema.ts +188 -1
- package/src/exit-codes.ts +42 -0
- package/src/index.ts +37 -0
- package/src/operations/tasks.ts +50 -0
- package/src/task.ts +39 -0
- package/src/validator/index.ts +385 -0
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validator role contracts — types consumed by the SDK tools (T10511) and the
|
|
3
|
+
* Max-N runtime (T10512) to express the verdict a Validator agent returns
|
|
4
|
+
* after reviewing a Worker submission against a task's acceptance criteria.
|
|
5
|
+
*
|
|
6
|
+
* The Validator is a NEW canonical role in the orchestration hierarchy,
|
|
7
|
+
* orthogonal to the existing Orchestrator → Lead → Worker triad. A Validator
|
|
8
|
+
* does NOT execute work and does NOT spawn other agents — it ONLY attests or
|
|
9
|
+
* rejects work claimed-done by another agent, per-AC.
|
|
10
|
+
*
|
|
11
|
+
* Identity triad:
|
|
12
|
+
* - `agentId` follows the pattern `validator-<discriminator>` (e.g.
|
|
13
|
+
* `validator-prime`, `validator-sec-001`).
|
|
14
|
+
* - `role` is `'validator'` (see {@link AgentRole}).
|
|
15
|
+
* - `sigilCard` is reused as-is from the existing peer/memory layer when
|
|
16
|
+
* the Validator is registered as a CANT-defined persona.
|
|
17
|
+
*
|
|
18
|
+
* Design constraints (ADR-055 / D028 boundary rules):
|
|
19
|
+
* - Lives in `packages/contracts/` — ZERO runtime dependencies (Zod only).
|
|
20
|
+
* - Consumed by `packages/cleo-sdk-tools/` (T10511) and `packages/core/` (T10512).
|
|
21
|
+
* - No cross-package relative imports.
|
|
22
|
+
*
|
|
23
|
+
* @module validator
|
|
24
|
+
* @task T10510
|
|
25
|
+
* @epic T10383 (E-VALIDATOR-ROLE)
|
|
26
|
+
* @saga T10377 (SG-IVTR-AC-BINDING)
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
import { z } from 'zod';
|
|
30
|
+
|
|
31
|
+
// ============================================================================
|
|
32
|
+
// AgentRole — canonical role enum with Validator extension
|
|
33
|
+
// ============================================================================
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Canonical agent role classification across the CLEO orchestration hierarchy.
|
|
37
|
+
*
|
|
38
|
+
* Mirrors the three-tier model in {@link PeerKind} / {@link AgentSpawnCapability}
|
|
39
|
+
* with an additional `validator` tier introduced by Saga T10377
|
|
40
|
+
* SG-IVTR-AC-BINDING:
|
|
41
|
+
*
|
|
42
|
+
* - `orchestrator` — coordinates multi-agent workflows; may spawn leads and workers
|
|
43
|
+
* - `lead` — specialist; dispatches workers only
|
|
44
|
+
* - `worker` — terminal executor; may not spawn
|
|
45
|
+
* - `validator` — terminal reviewer; attests or rejects another agent's
|
|
46
|
+
* work against the task's acceptance criteria. Does NOT
|
|
47
|
+
* execute work and does NOT spawn other agents.
|
|
48
|
+
*
|
|
49
|
+
* This type is ADDITIVE to the existing per-file role unions
|
|
50
|
+
* (`PeerKind` in `peer.ts`, `AgentSpawnCapability` in `agent-registry-v3.ts`,
|
|
51
|
+
* `CLEO_AGENT_ROLE` in `branch-lock.ts`, etc.). Existing unions are
|
|
52
|
+
* intentionally UNCHANGED — call sites that need the Validator role
|
|
53
|
+
* import {@link AgentRole} from this module.
|
|
54
|
+
*
|
|
55
|
+
* @task T10510
|
|
56
|
+
* @epic T10383
|
|
57
|
+
*/
|
|
58
|
+
export type AgentRole = 'orchestrator' | 'lead' | 'worker' | 'validator';
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Frozen list of canonical {@link AgentRole} values for runtime iteration
|
|
62
|
+
* (e.g. CLI flag validation, registry seeding).
|
|
63
|
+
*
|
|
64
|
+
* @task T10510
|
|
65
|
+
* @epic T10383
|
|
66
|
+
*/
|
|
67
|
+
export const AGENT_ROLES = ['orchestrator', 'lead', 'worker', 'validator'] as const;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Type guard for {@link AgentRole}. Returns `true` when `value` is one of the
|
|
71
|
+
* four canonical role strings.
|
|
72
|
+
*
|
|
73
|
+
* @task T10510
|
|
74
|
+
*/
|
|
75
|
+
export function isAgentRole(value: unknown): value is AgentRole {
|
|
76
|
+
return typeof value === 'string' && (AGENT_ROLES as readonly string[]).includes(value);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// ============================================================================
|
|
80
|
+
// ValidatorFinding — per-AC reasoning row
|
|
81
|
+
// ============================================================================
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Status of a single Validator finding against one acceptance criterion.
|
|
85
|
+
*
|
|
86
|
+
* - `pass` — the AC is satisfied by the Worker's submission
|
|
87
|
+
* - `fail` — the AC is NOT satisfied; the Validator rejects this AC
|
|
88
|
+
* - `inconclusive` — the Validator cannot determine pass/fail from the
|
|
89
|
+
* evidence presented (e.g. missing artifact, ambiguous
|
|
90
|
+
* wording). A Verdict containing any `inconclusive`
|
|
91
|
+
* finding is treated as a {@link ValidatorRejection} —
|
|
92
|
+
* the Worker MUST clarify and re-submit.
|
|
93
|
+
*
|
|
94
|
+
* @task T10510
|
|
95
|
+
*/
|
|
96
|
+
export type ValidatorFindingStatus = 'pass' | 'fail' | 'inconclusive';
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Single Validator finding scoped to one acceptance criterion.
|
|
100
|
+
*
|
|
101
|
+
* Produced per-AC during validator review and aggregated into either a
|
|
102
|
+
* {@link ValidatorAttestation} (all pass) or {@link ValidatorRejection}
|
|
103
|
+
* (any fail or inconclusive). Each finding carries the AC's stable UUID
|
|
104
|
+
* (matching `task_acceptance_criteria.id` — see {@link AcRow}) so downstream
|
|
105
|
+
* persistence (T10503 / T10509 AC-bindings) can join findings to ACs
|
|
106
|
+
* without depending on ordinal position.
|
|
107
|
+
*
|
|
108
|
+
* @task T10510
|
|
109
|
+
* @epic T10383
|
|
110
|
+
*/
|
|
111
|
+
export interface ValidatorFinding {
|
|
112
|
+
/**
|
|
113
|
+
* Stable AC identifier (UUIDv4) matching
|
|
114
|
+
* `task_acceptance_criteria.id`. See {@link AcRow.id}.
|
|
115
|
+
*/
|
|
116
|
+
acId: string;
|
|
117
|
+
/** Pass / fail / inconclusive verdict for this AC. */
|
|
118
|
+
status: ValidatorFindingStatus;
|
|
119
|
+
/**
|
|
120
|
+
* Free-text reasoning explaining the verdict. Required for `fail` and
|
|
121
|
+
* `inconclusive`; recommended (but optional via the runtime guard) for
|
|
122
|
+
* `pass` so reviewers can audit the chain-of-thought later.
|
|
123
|
+
*/
|
|
124
|
+
reasoning: string;
|
|
125
|
+
/**
|
|
126
|
+
* Optional evidence references that backed this finding — e.g. test
|
|
127
|
+
* file paths, commit shas, doc URLs. Free-form strings; the schema
|
|
128
|
+
* intentionally does NOT validate format here so the SDK tools layer
|
|
129
|
+
* (T10511) can shape its own evidence-atom narrowing.
|
|
130
|
+
*/
|
|
131
|
+
evidenceRefs?: string[];
|
|
132
|
+
/** ISO-8601 timestamp at which this finding was recorded. */
|
|
133
|
+
checkedAt: string;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// ============================================================================
|
|
137
|
+
// ValidatorAttestation — accept verdict envelope
|
|
138
|
+
// ============================================================================
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Envelope returned by a Validator when the Worker's submission passes
|
|
142
|
+
* EVERY acceptance criterion on the task.
|
|
143
|
+
*
|
|
144
|
+
* Discriminated by `verdict: 'attest'`. When at least one finding has
|
|
145
|
+
* status `'fail'` or `'inconclusive'`, the Validator MUST emit a
|
|
146
|
+
* {@link ValidatorRejection} instead — never a partial-pass attestation.
|
|
147
|
+
*
|
|
148
|
+
* @task T10510
|
|
149
|
+
* @epic T10383
|
|
150
|
+
*/
|
|
151
|
+
export interface ValidatorAttestation {
|
|
152
|
+
/** Discriminant — always `'attest'`. */
|
|
153
|
+
verdict: 'attest';
|
|
154
|
+
/** Task being attested. Matches `tasks.id`. */
|
|
155
|
+
taskId: string;
|
|
156
|
+
/**
|
|
157
|
+
* Stable Validator agent identifier following the pattern
|
|
158
|
+
* `validator-<discriminator>` (e.g. `validator-prime`).
|
|
159
|
+
*/
|
|
160
|
+
validatorId: string;
|
|
161
|
+
/**
|
|
162
|
+
* Per-AC findings. MUST contain one entry per AC on the task; EVERY
|
|
163
|
+
* entry MUST have `status: 'pass'`. The runtime validator
|
|
164
|
+
* ({@link isValidatorAttestation}) enforces this invariant.
|
|
165
|
+
*/
|
|
166
|
+
findings: ValidatorFinding[];
|
|
167
|
+
/**
|
|
168
|
+
* Optional summary paragraph the Validator may include to capture
|
|
169
|
+
* holistic observations (e.g. "all 5 ACs pass; the implementation
|
|
170
|
+
* also tightened the error message in foo.ts which I checked").
|
|
171
|
+
*/
|
|
172
|
+
summary?: string;
|
|
173
|
+
/** ISO-8601 timestamp at which the verdict was finalized. */
|
|
174
|
+
attestedAt: string;
|
|
175
|
+
/**
|
|
176
|
+
* Schema version for forward-compatible evolution. Pin to `'1'` for
|
|
177
|
+
* the initial T10510 contract.
|
|
178
|
+
*/
|
|
179
|
+
schemaVersion: '1';
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// ============================================================================
|
|
183
|
+
// ValidatorRejection — refuse verdict envelope
|
|
184
|
+
// ============================================================================
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Envelope returned by a Validator when at least one acceptance criterion
|
|
188
|
+
* fails or is inconclusive.
|
|
189
|
+
*
|
|
190
|
+
* Discriminated by `verdict: 'reject'`. The `findings` array MUST contain
|
|
191
|
+
* AT LEAST ONE entry with status `'fail'` or `'inconclusive'`; otherwise
|
|
192
|
+
* the Validator should have emitted a {@link ValidatorAttestation}.
|
|
193
|
+
*
|
|
194
|
+
* @task T10510
|
|
195
|
+
* @epic T10383
|
|
196
|
+
*/
|
|
197
|
+
export interface ValidatorRejection {
|
|
198
|
+
/** Discriminant — always `'reject'`. */
|
|
199
|
+
verdict: 'reject';
|
|
200
|
+
/** Task being rejected. Matches `tasks.id`. */
|
|
201
|
+
taskId: string;
|
|
202
|
+
/**
|
|
203
|
+
* Stable Validator agent identifier following the pattern
|
|
204
|
+
* `validator-<discriminator>` (e.g. `validator-prime`).
|
|
205
|
+
*/
|
|
206
|
+
validatorId: string;
|
|
207
|
+
/**
|
|
208
|
+
* Per-AC findings. MUST contain one entry per AC on the task; AT
|
|
209
|
+
* LEAST ONE entry MUST have `status: 'fail'` or `'inconclusive'`.
|
|
210
|
+
*/
|
|
211
|
+
findings: ValidatorFinding[];
|
|
212
|
+
/**
|
|
213
|
+
* Required summary paragraph describing the high-level reason for
|
|
214
|
+
* rejection. Distinct from per-AC `reasoning` — this is the
|
|
215
|
+
* human-facing message shown when the Worker is told to revise.
|
|
216
|
+
*/
|
|
217
|
+
summary: string;
|
|
218
|
+
/**
|
|
219
|
+
* Suggested remediation steps. Free-form list of strings — the SDK
|
|
220
|
+
* tools layer (T10511) is responsible for any structured shape.
|
|
221
|
+
*/
|
|
222
|
+
remediationHints?: string[];
|
|
223
|
+
/** ISO-8601 timestamp at which the verdict was finalized. */
|
|
224
|
+
rejectedAt: string;
|
|
225
|
+
/**
|
|
226
|
+
* Schema version for forward-compatible evolution. Pin to `'1'` for
|
|
227
|
+
* the initial T10510 contract.
|
|
228
|
+
*/
|
|
229
|
+
schemaVersion: '1';
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// ============================================================================
|
|
233
|
+
// ValidatorVerdict — discriminated union
|
|
234
|
+
// ============================================================================
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Discriminated union over the two Validator outcomes. Narrow via the
|
|
238
|
+
* `verdict` discriminant:
|
|
239
|
+
*
|
|
240
|
+
* ```ts
|
|
241
|
+
* function handle(v: ValidatorVerdict) {
|
|
242
|
+
* if (v.verdict === 'attest') {
|
|
243
|
+
* // v is ValidatorAttestation — every finding passes
|
|
244
|
+
* } else {
|
|
245
|
+
* // v is ValidatorRejection — summary + remediationHints available
|
|
246
|
+
* }
|
|
247
|
+
* }
|
|
248
|
+
* ```
|
|
249
|
+
*
|
|
250
|
+
* @task T10510
|
|
251
|
+
* @epic T10383
|
|
252
|
+
*/
|
|
253
|
+
export type ValidatorVerdict = ValidatorAttestation | ValidatorRejection;
|
|
254
|
+
|
|
255
|
+
// ============================================================================
|
|
256
|
+
// Zod schemas — runtime validation
|
|
257
|
+
// ============================================================================
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Regex validating the canonical Validator agentId pattern
|
|
261
|
+
* `validator-<discriminator>`. The discriminator is `[a-z0-9][a-z0-9-]*`
|
|
262
|
+
* (lowercase alphanumeric + hyphens, must start with alphanum).
|
|
263
|
+
*
|
|
264
|
+
* Examples that MATCH: `validator-prime`, `validator-sec-001`, `validator-a`.
|
|
265
|
+
* Examples that DO NOT match: `validator-`, `Validator-prime`, `validator--x`.
|
|
266
|
+
*
|
|
267
|
+
* @task T10510
|
|
268
|
+
*/
|
|
269
|
+
export const VALIDATOR_ID_REGEX = /^validator-[a-z0-9][a-z0-9-]*$/;
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Zod schema for {@link ValidatorFinding}. Enforces the AC-id presence,
|
|
273
|
+
* status enum, non-empty reasoning, and ISO-8601 timestamp shape.
|
|
274
|
+
*
|
|
275
|
+
* @task T10510
|
|
276
|
+
*/
|
|
277
|
+
export const validatorFindingSchema = z.object({
|
|
278
|
+
acId: z.string().min(1, 'acId must be non-empty'),
|
|
279
|
+
status: z.enum(['pass', 'fail', 'inconclusive']),
|
|
280
|
+
reasoning: z.string().min(1, 'reasoning must be non-empty'),
|
|
281
|
+
evidenceRefs: z.array(z.string()).optional(),
|
|
282
|
+
checkedAt: z.string().min(1, 'checkedAt must be a non-empty ISO-8601 string'),
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Zod schema for {@link ValidatorAttestation}. Enforces:
|
|
287
|
+
* - `verdict === 'attest'`
|
|
288
|
+
* - `validatorId` matches {@link VALIDATOR_ID_REGEX}
|
|
289
|
+
* - `findings` non-empty AND every entry has `status === 'pass'`
|
|
290
|
+
* - `schemaVersion === '1'`
|
|
291
|
+
*
|
|
292
|
+
* @task T10510
|
|
293
|
+
*/
|
|
294
|
+
export const validatorAttestationSchema = z.object({
|
|
295
|
+
verdict: z.literal('attest'),
|
|
296
|
+
taskId: z.string().min(1),
|
|
297
|
+
validatorId: z
|
|
298
|
+
.string()
|
|
299
|
+
.regex(VALIDATOR_ID_REGEX, 'validatorId must match the pattern validator-<discriminator>'),
|
|
300
|
+
findings: z
|
|
301
|
+
.array(validatorFindingSchema)
|
|
302
|
+
.min(1, 'attestation must contain at least one finding')
|
|
303
|
+
.refine(
|
|
304
|
+
(findings) => findings.every((f) => f.status === 'pass'),
|
|
305
|
+
'attestation requires every finding to have status="pass"',
|
|
306
|
+
),
|
|
307
|
+
summary: z.string().optional(),
|
|
308
|
+
attestedAt: z.string().min(1),
|
|
309
|
+
schemaVersion: z.literal('1'),
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Zod schema for {@link ValidatorRejection}. Enforces:
|
|
314
|
+
* - `verdict === 'reject'`
|
|
315
|
+
* - `validatorId` matches {@link VALIDATOR_ID_REGEX}
|
|
316
|
+
* - `findings` non-empty AND at least one entry has `status !== 'pass'`
|
|
317
|
+
* - `summary` non-empty (rejection rationale is mandatory)
|
|
318
|
+
* - `schemaVersion === '1'`
|
|
319
|
+
*
|
|
320
|
+
* @task T10510
|
|
321
|
+
*/
|
|
322
|
+
export const validatorRejectionSchema = z.object({
|
|
323
|
+
verdict: z.literal('reject'),
|
|
324
|
+
taskId: z.string().min(1),
|
|
325
|
+
validatorId: z
|
|
326
|
+
.string()
|
|
327
|
+
.regex(VALIDATOR_ID_REGEX, 'validatorId must match the pattern validator-<discriminator>'),
|
|
328
|
+
findings: z
|
|
329
|
+
.array(validatorFindingSchema)
|
|
330
|
+
.min(1, 'rejection must contain at least one finding')
|
|
331
|
+
.refine(
|
|
332
|
+
(findings) => findings.some((f) => f.status !== 'pass'),
|
|
333
|
+
'rejection requires at least one finding with status "fail" or "inconclusive"',
|
|
334
|
+
),
|
|
335
|
+
summary: z.string().min(1, 'rejection summary must be non-empty'),
|
|
336
|
+
remediationHints: z.array(z.string()).optional(),
|
|
337
|
+
rejectedAt: z.string().min(1),
|
|
338
|
+
schemaVersion: z.literal('1'),
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Zod schema for the {@link ValidatorVerdict} discriminated union.
|
|
343
|
+
* Routes parsing through the `verdict` discriminant.
|
|
344
|
+
*
|
|
345
|
+
* @task T10510
|
|
346
|
+
*/
|
|
347
|
+
export const validatorVerdictSchema = z.discriminatedUnion('verdict', [
|
|
348
|
+
validatorAttestationSchema,
|
|
349
|
+
validatorRejectionSchema,
|
|
350
|
+
]);
|
|
351
|
+
|
|
352
|
+
// ============================================================================
|
|
353
|
+
// Type guards
|
|
354
|
+
// ============================================================================
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Type guard validating that `value` conforms to {@link ValidatorAttestation}.
|
|
358
|
+
* Internally uses {@link validatorAttestationSchema}.
|
|
359
|
+
*
|
|
360
|
+
* @task T10510
|
|
361
|
+
*/
|
|
362
|
+
export function isValidatorAttestation(value: unknown): value is ValidatorAttestation {
|
|
363
|
+
return validatorAttestationSchema.safeParse(value).success;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* Type guard validating that `value` conforms to {@link ValidatorRejection}.
|
|
368
|
+
* Internally uses {@link validatorRejectionSchema}.
|
|
369
|
+
*
|
|
370
|
+
* @task T10510
|
|
371
|
+
*/
|
|
372
|
+
export function isValidatorRejection(value: unknown): value is ValidatorRejection {
|
|
373
|
+
return validatorRejectionSchema.safeParse(value).success;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* Type guard validating that `value` conforms to {@link ValidatorVerdict}
|
|
378
|
+
* (either an attestation or a rejection). Internally uses
|
|
379
|
+
* {@link validatorVerdictSchema}.
|
|
380
|
+
*
|
|
381
|
+
* @task T10510
|
|
382
|
+
*/
|
|
383
|
+
export function isValidatorVerdict(value: unknown): value is ValidatorVerdict {
|
|
384
|
+
return validatorVerdictSchema.safeParse(value).success;
|
|
385
|
+
}
|