@archmax-ai/harness 1.0.1 → 2.0.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.
- package/README.md +167 -165
- package/dist/agent.d.ts +2 -2
- package/dist/agent.js +2 -2
- package/dist/assembly/compose.js +5 -3
- package/dist/assembly/compose.js.map +1 -1
- package/dist/authoring-skill/archmax-harness/README.md +13 -15
- package/dist/authoring-skill/archmax-harness/SKILL.md +174 -261
- package/dist/authoring-skill/archmax-harness/references/backend-integration.md +244 -764
- package/dist/authoring-skill/archmax-harness/references/cases.md +178 -53
- package/dist/authoring-skill/archmax-harness/references/code-states-and-scripts.md +211 -100
- package/dist/authoring-skill/archmax-harness/references/diagnostics.md +57 -78
- package/dist/authoring-skill/archmax-harness/references/workflow-schema.md +614 -1977
- package/dist/core/attachments.d.ts +2 -0
- package/dist/core/attachments.js +2 -1
- package/dist/core/attachments.js.map +1 -1
- package/dist/core/deepagents.d.ts +1 -2
- package/dist/core/deepagents.js +1 -2
- package/dist/core/deepagents.js.map +1 -1
- package/dist/core/file-operations.d.ts +6 -0
- package/dist/core/file-operations.js +29 -0
- package/dist/core/file-operations.js.map +1 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/kernel/kernel.js +2 -2
- package/dist/kernel/kernel.js.map +1 -1
- package/dist/machine/lint-spec.js +6 -5
- package/dist/machine/lint-spec.js.map +1 -1
- package/dist/machine/machine.d.ts +8 -10
- package/dist/machine/machine.js +11 -12
- package/dist/machine/machine.js.map +1 -1
- package/dist/machine/signature-pattern.d.ts +24 -0
- package/dist/machine/signature-pattern.js +67 -0
- package/dist/machine/signature-pattern.js.map +1 -0
- package/dist/machine/signature.d.ts +137 -35
- package/dist/machine/signature.js +476 -25
- package/dist/machine/signature.js.map +1 -1
- package/dist/machine/spec-schema.d.ts +203 -5
- package/dist/machine/spec-schema.js +209 -51
- package/dist/machine/spec-schema.js.map +1 -1
- package/dist/machine/tool-names.d.ts +24 -34
- package/dist/machine/tool-names.js +43 -63
- package/dist/machine/tool-names.js.map +1 -1
- package/dist/machine/types.d.ts +11 -10
- package/dist/machine/types.js.map +1 -1
- package/dist/public/spec.d.ts +10 -6
- package/dist/public/spec.js +9 -5
- package/dist/public/spec.js.map +1 -1
- package/dist/validate/validate.js +3 -3
- package/dist/validate/validate.js.map +1 -1
- package/dist/workflow/automated.js +6 -1
- package/dist/workflow/automated.js.map +1 -1
- package/dist/workflow/control-tools.js +3 -3
- package/dist/workflow/control-tools.js.map +1 -1
- package/dist/workflow/file-values.d.ts +4 -1
- package/dist/workflow/file-values.js +2 -0
- package/dist/workflow/file-values.js.map +1 -1
- package/dist/workflow/grader-state.d.ts +2 -2
- package/dist/workflow/grader-state.js +5 -15
- package/dist/workflow/grader-state.js.map +1 -1
- package/dist/workflow/middleware.d.ts +2 -0
- package/dist/workflow/middleware.js +3 -26
- package/dist/workflow/middleware.js.map +1 -1
- package/dist/workflow/on-error.js +0 -1
- package/dist/workflow/on-error.js.map +1 -1
- package/dist/workflow/parks.js +0 -1
- package/dist/workflow/parks.js.map +1 -1
- package/dist/workflow/render-prompt.d.ts +4 -5
- package/dist/workflow/render-prompt.js +31 -29
- package/dist/workflow/render-prompt.js.map +1 -1
- package/dist/workflow/signature-checks.d.ts +8 -3
- package/dist/workflow/signature-checks.js +17 -4
- package/dist/workflow/signature-checks.js.map +1 -1
- package/dist/workflow/state.d.ts +0 -9
- package/dist/workflow/state.js +0 -3
- package/dist/workflow/state.js.map +1 -1
- package/dist/workflow/sub-workflow.js +4 -1
- package/dist/workflow/sub-workflow.js.map +1 -1
- package/dist/workflow/turn-boundary.js +0 -1
- package/dist/workflow/turn-boundary.js.map +1 -1
- package/package.json +2 -2
- package/skills/archmax-harness/README.md +13 -15
- package/skills/archmax-harness/SKILL.md +174 -261
- package/skills/archmax-harness/references/backend-integration.md +244 -764
- package/skills/archmax-harness/references/cases.md +178 -53
- package/skills/archmax-harness/references/code-states-and-scripts.md +211 -100
- package/skills/archmax-harness/references/diagnostics.md +57 -78
- package/skills/archmax-harness/references/workflow-schema.md +614 -1977
- package/dist/authoring-skill/archmax-harness/references/code-state-and-test-scripts.md +0 -1424
- package/dist/authoring-skill/archmax-harness/references/wiring.md +0 -119
- package/dist/authoring-skill/archmax-harness/references/workflow-yaml.md +0 -399
- package/skills/archmax-harness/references/code-state-and-test-scripts.md +0 -1424
- package/skills/archmax-harness/references/wiring.md +0 -119
- package/skills/archmax-harness/references/workflow-yaml.md +0 -399
|
@@ -57,13 +57,12 @@ export declare const machineTransitionSchema: z.ZodObject<{
|
|
|
57
57
|
none: "none";
|
|
58
58
|
}>>;
|
|
59
59
|
}, z.core.$strict>;
|
|
60
|
-
/** Whether an edge carries no description
|
|
60
|
+
/** Whether an edge carries no description: absent, empty or blank. */
|
|
61
61
|
export declare function isUndescribed(transition: {
|
|
62
62
|
description?: string;
|
|
63
63
|
}): boolean;
|
|
64
64
|
/** @see StateBudget */
|
|
65
65
|
export declare const stateBudgetSchema: z.ZodObject<{
|
|
66
|
-
maxTurns: z.ZodOptional<z.ZodNumber>;
|
|
67
66
|
timeoutMs: z.ZodOptional<z.ZodNumber>;
|
|
68
67
|
maxParks: z.ZodOptional<z.ZodNumber>;
|
|
69
68
|
}, z.core.$strict>;
|
|
@@ -173,7 +172,8 @@ export declare const stateTypeSchema: z.ZodEnum<{
|
|
|
173
172
|
*
|
|
174
173
|
* The object spelling of a signature entry. Strict, unlike the loose declaration
|
|
175
174
|
* around it: an unknown key here is most likely a misspelled `type` or
|
|
176
|
-
* `description`, and read loosely it would silently mean "untyped".
|
|
175
|
+
* `description`, and read loosely it would silently mean "untyped". A rule key
|
|
176
|
+
* is shape-checked here and held to its entry's type by {@link refineRules}.
|
|
177
177
|
*/
|
|
178
178
|
export declare const signatureEntryObjectSchema: z.ZodObject<{
|
|
179
179
|
name: z.ZodString;
|
|
@@ -189,6 +189,26 @@ export declare const signatureEntryObjectSchema: z.ZodObject<{
|
|
|
189
189
|
array: "array";
|
|
190
190
|
}>>;
|
|
191
191
|
description: z.ZodOptional<z.ZodString>;
|
|
192
|
+
format: z.ZodOptional<z.ZodEnum<{
|
|
193
|
+
email: "email";
|
|
194
|
+
url: "url";
|
|
195
|
+
phone: "phone";
|
|
196
|
+
}>>;
|
|
197
|
+
pattern: z.ZodOptional<z.ZodString>;
|
|
198
|
+
minLength: z.ZodOptional<z.ZodNumber>;
|
|
199
|
+
maxLength: z.ZodOptional<z.ZodNumber>;
|
|
200
|
+
enum: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
201
|
+
content: z.ZodOptional<z.ZodEnum<{
|
|
202
|
+
text: "text";
|
|
203
|
+
markdown: "markdown";
|
|
204
|
+
html: "html";
|
|
205
|
+
}>>;
|
|
206
|
+
minimum: z.ZodOptional<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>>;
|
|
207
|
+
maximum: z.ZodOptional<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>>;
|
|
208
|
+
minItems: z.ZodOptional<z.ZodNumber>;
|
|
209
|
+
maxItems: z.ZodOptional<z.ZodNumber>;
|
|
210
|
+
accept: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
211
|
+
maxSize: z.ZodOptional<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>>;
|
|
192
212
|
}, z.core.$strict>;
|
|
193
213
|
/**
|
|
194
214
|
* @see SignatureEntryDeclaration
|
|
@@ -211,6 +231,26 @@ export declare const signatureEntrySchema: z.ZodUnion<readonly [z.ZodString, z.Z
|
|
|
211
231
|
array: "array";
|
|
212
232
|
}>>;
|
|
213
233
|
description: z.ZodOptional<z.ZodString>;
|
|
234
|
+
format: z.ZodOptional<z.ZodEnum<{
|
|
235
|
+
email: "email";
|
|
236
|
+
url: "url";
|
|
237
|
+
phone: "phone";
|
|
238
|
+
}>>;
|
|
239
|
+
pattern: z.ZodOptional<z.ZodString>;
|
|
240
|
+
minLength: z.ZodOptional<z.ZodNumber>;
|
|
241
|
+
maxLength: z.ZodOptional<z.ZodNumber>;
|
|
242
|
+
enum: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
243
|
+
content: z.ZodOptional<z.ZodEnum<{
|
|
244
|
+
text: "text";
|
|
245
|
+
markdown: "markdown";
|
|
246
|
+
html: "html";
|
|
247
|
+
}>>;
|
|
248
|
+
minimum: z.ZodOptional<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>>;
|
|
249
|
+
maximum: z.ZodOptional<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>>;
|
|
250
|
+
minItems: z.ZodOptional<z.ZodNumber>;
|
|
251
|
+
maxItems: z.ZodOptional<z.ZodNumber>;
|
|
252
|
+
accept: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
253
|
+
maxSize: z.ZodOptional<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>>;
|
|
214
254
|
}, z.core.$strict>]>;
|
|
215
255
|
/**
|
|
216
256
|
* @see TriggerDeclaration
|
|
@@ -238,6 +278,26 @@ export declare const triggerDeclarationSchema: z.ZodObject<{
|
|
|
238
278
|
array: "array";
|
|
239
279
|
}>>;
|
|
240
280
|
description: z.ZodOptional<z.ZodString>;
|
|
281
|
+
format: z.ZodOptional<z.ZodEnum<{
|
|
282
|
+
email: "email";
|
|
283
|
+
url: "url";
|
|
284
|
+
phone: "phone";
|
|
285
|
+
}>>;
|
|
286
|
+
pattern: z.ZodOptional<z.ZodString>;
|
|
287
|
+
minLength: z.ZodOptional<z.ZodNumber>;
|
|
288
|
+
maxLength: z.ZodOptional<z.ZodNumber>;
|
|
289
|
+
enum: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
290
|
+
content: z.ZodOptional<z.ZodEnum<{
|
|
291
|
+
text: "text";
|
|
292
|
+
markdown: "markdown";
|
|
293
|
+
html: "html";
|
|
294
|
+
}>>;
|
|
295
|
+
minimum: z.ZodOptional<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>>;
|
|
296
|
+
maximum: z.ZodOptional<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>>;
|
|
297
|
+
minItems: z.ZodOptional<z.ZodNumber>;
|
|
298
|
+
maxItems: z.ZodOptional<z.ZodNumber>;
|
|
299
|
+
accept: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
300
|
+
maxSize: z.ZodOptional<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>>;
|
|
241
301
|
}, z.core.$strict>]>>>;
|
|
242
302
|
returns: z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
|
|
243
303
|
name: z.ZodString;
|
|
@@ -253,6 +313,26 @@ export declare const triggerDeclarationSchema: z.ZodObject<{
|
|
|
253
313
|
array: "array";
|
|
254
314
|
}>>;
|
|
255
315
|
description: z.ZodOptional<z.ZodString>;
|
|
316
|
+
format: z.ZodOptional<z.ZodEnum<{
|
|
317
|
+
email: "email";
|
|
318
|
+
url: "url";
|
|
319
|
+
phone: "phone";
|
|
320
|
+
}>>;
|
|
321
|
+
pattern: z.ZodOptional<z.ZodString>;
|
|
322
|
+
minLength: z.ZodOptional<z.ZodNumber>;
|
|
323
|
+
maxLength: z.ZodOptional<z.ZodNumber>;
|
|
324
|
+
enum: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
325
|
+
content: z.ZodOptional<z.ZodEnum<{
|
|
326
|
+
text: "text";
|
|
327
|
+
markdown: "markdown";
|
|
328
|
+
html: "html";
|
|
329
|
+
}>>;
|
|
330
|
+
minimum: z.ZodOptional<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>>;
|
|
331
|
+
maximum: z.ZodOptional<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>>;
|
|
332
|
+
minItems: z.ZodOptional<z.ZodNumber>;
|
|
333
|
+
maxItems: z.ZodOptional<z.ZodNumber>;
|
|
334
|
+
accept: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
335
|
+
maxSize: z.ZodOptional<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>>;
|
|
256
336
|
}, z.core.$strict>]>>>;
|
|
257
337
|
}, z.core.$loose>;
|
|
258
338
|
/**
|
|
@@ -280,6 +360,26 @@ export declare const stateTriggersSchema: z.ZodRecord<z.ZodString, z.ZodNullable
|
|
|
280
360
|
array: "array";
|
|
281
361
|
}>>;
|
|
282
362
|
description: z.ZodOptional<z.ZodString>;
|
|
363
|
+
format: z.ZodOptional<z.ZodEnum<{
|
|
364
|
+
email: "email";
|
|
365
|
+
url: "url";
|
|
366
|
+
phone: "phone";
|
|
367
|
+
}>>;
|
|
368
|
+
pattern: z.ZodOptional<z.ZodString>;
|
|
369
|
+
minLength: z.ZodOptional<z.ZodNumber>;
|
|
370
|
+
maxLength: z.ZodOptional<z.ZodNumber>;
|
|
371
|
+
enum: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
372
|
+
content: z.ZodOptional<z.ZodEnum<{
|
|
373
|
+
text: "text";
|
|
374
|
+
markdown: "markdown";
|
|
375
|
+
html: "html";
|
|
376
|
+
}>>;
|
|
377
|
+
minimum: z.ZodOptional<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>>;
|
|
378
|
+
maximum: z.ZodOptional<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>>;
|
|
379
|
+
minItems: z.ZodOptional<z.ZodNumber>;
|
|
380
|
+
maxItems: z.ZodOptional<z.ZodNumber>;
|
|
381
|
+
accept: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
382
|
+
maxSize: z.ZodOptional<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>>;
|
|
283
383
|
}, z.core.$strict>]>>>;
|
|
284
384
|
returns: z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
|
|
285
385
|
name: z.ZodString;
|
|
@@ -295,6 +395,26 @@ export declare const stateTriggersSchema: z.ZodRecord<z.ZodString, z.ZodNullable
|
|
|
295
395
|
array: "array";
|
|
296
396
|
}>>;
|
|
297
397
|
description: z.ZodOptional<z.ZodString>;
|
|
398
|
+
format: z.ZodOptional<z.ZodEnum<{
|
|
399
|
+
email: "email";
|
|
400
|
+
url: "url";
|
|
401
|
+
phone: "phone";
|
|
402
|
+
}>>;
|
|
403
|
+
pattern: z.ZodOptional<z.ZodString>;
|
|
404
|
+
minLength: z.ZodOptional<z.ZodNumber>;
|
|
405
|
+
maxLength: z.ZodOptional<z.ZodNumber>;
|
|
406
|
+
enum: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
407
|
+
content: z.ZodOptional<z.ZodEnum<{
|
|
408
|
+
text: "text";
|
|
409
|
+
markdown: "markdown";
|
|
410
|
+
html: "html";
|
|
411
|
+
}>>;
|
|
412
|
+
minimum: z.ZodOptional<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>>;
|
|
413
|
+
maximum: z.ZodOptional<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>>;
|
|
414
|
+
minItems: z.ZodOptional<z.ZodNumber>;
|
|
415
|
+
maxItems: z.ZodOptional<z.ZodNumber>;
|
|
416
|
+
accept: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
417
|
+
maxSize: z.ZodOptional<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>>;
|
|
298
418
|
}, z.core.$strict>]>>>;
|
|
299
419
|
}, z.core.$loose>>>;
|
|
300
420
|
/** @see MachineState */
|
|
@@ -329,6 +449,26 @@ export declare const machineStateSchema: z.ZodObject<{
|
|
|
329
449
|
array: "array";
|
|
330
450
|
}>>;
|
|
331
451
|
description: z.ZodOptional<z.ZodString>;
|
|
452
|
+
format: z.ZodOptional<z.ZodEnum<{
|
|
453
|
+
email: "email";
|
|
454
|
+
url: "url";
|
|
455
|
+
phone: "phone";
|
|
456
|
+
}>>;
|
|
457
|
+
pattern: z.ZodOptional<z.ZodString>;
|
|
458
|
+
minLength: z.ZodOptional<z.ZodNumber>;
|
|
459
|
+
maxLength: z.ZodOptional<z.ZodNumber>;
|
|
460
|
+
enum: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
461
|
+
content: z.ZodOptional<z.ZodEnum<{
|
|
462
|
+
text: "text";
|
|
463
|
+
markdown: "markdown";
|
|
464
|
+
html: "html";
|
|
465
|
+
}>>;
|
|
466
|
+
minimum: z.ZodOptional<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>>;
|
|
467
|
+
maximum: z.ZodOptional<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>>;
|
|
468
|
+
minItems: z.ZodOptional<z.ZodNumber>;
|
|
469
|
+
maxItems: z.ZodOptional<z.ZodNumber>;
|
|
470
|
+
accept: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
471
|
+
maxSize: z.ZodOptional<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>>;
|
|
332
472
|
}, z.core.$strict>]>>>;
|
|
333
473
|
returns: z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
|
|
334
474
|
name: z.ZodString;
|
|
@@ -344,11 +484,30 @@ export declare const machineStateSchema: z.ZodObject<{
|
|
|
344
484
|
array: "array";
|
|
345
485
|
}>>;
|
|
346
486
|
description: z.ZodOptional<z.ZodString>;
|
|
487
|
+
format: z.ZodOptional<z.ZodEnum<{
|
|
488
|
+
email: "email";
|
|
489
|
+
url: "url";
|
|
490
|
+
phone: "phone";
|
|
491
|
+
}>>;
|
|
492
|
+
pattern: z.ZodOptional<z.ZodString>;
|
|
493
|
+
minLength: z.ZodOptional<z.ZodNumber>;
|
|
494
|
+
maxLength: z.ZodOptional<z.ZodNumber>;
|
|
495
|
+
enum: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
496
|
+
content: z.ZodOptional<z.ZodEnum<{
|
|
497
|
+
text: "text";
|
|
498
|
+
markdown: "markdown";
|
|
499
|
+
html: "html";
|
|
500
|
+
}>>;
|
|
501
|
+
minimum: z.ZodOptional<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>>;
|
|
502
|
+
maximum: z.ZodOptional<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>>;
|
|
503
|
+
minItems: z.ZodOptional<z.ZodNumber>;
|
|
504
|
+
maxItems: z.ZodOptional<z.ZodNumber>;
|
|
505
|
+
accept: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
506
|
+
maxSize: z.ZodOptional<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>>;
|
|
347
507
|
}, z.core.$strict>]>>>;
|
|
348
508
|
}, z.core.$loose>>>>;
|
|
349
509
|
script: z.ZodOptional<z.ZodString>;
|
|
350
510
|
budget: z.ZodOptional<z.ZodObject<{
|
|
351
|
-
maxTurns: z.ZodOptional<z.ZodNumber>;
|
|
352
511
|
timeoutMs: z.ZodOptional<z.ZodNumber>;
|
|
353
512
|
maxParks: z.ZodOptional<z.ZodNumber>;
|
|
354
513
|
}, z.core.$strict>>;
|
|
@@ -586,6 +745,26 @@ export declare const machineSpecSchema: z.ZodObject<{
|
|
|
586
745
|
array: "array";
|
|
587
746
|
}>>;
|
|
588
747
|
description: z.ZodOptional<z.ZodString>;
|
|
748
|
+
format: z.ZodOptional<z.ZodEnum<{
|
|
749
|
+
email: "email";
|
|
750
|
+
url: "url";
|
|
751
|
+
phone: "phone";
|
|
752
|
+
}>>;
|
|
753
|
+
pattern: z.ZodOptional<z.ZodString>;
|
|
754
|
+
minLength: z.ZodOptional<z.ZodNumber>;
|
|
755
|
+
maxLength: z.ZodOptional<z.ZodNumber>;
|
|
756
|
+
enum: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
757
|
+
content: z.ZodOptional<z.ZodEnum<{
|
|
758
|
+
text: "text";
|
|
759
|
+
markdown: "markdown";
|
|
760
|
+
html: "html";
|
|
761
|
+
}>>;
|
|
762
|
+
minimum: z.ZodOptional<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>>;
|
|
763
|
+
maximum: z.ZodOptional<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>>;
|
|
764
|
+
minItems: z.ZodOptional<z.ZodNumber>;
|
|
765
|
+
maxItems: z.ZodOptional<z.ZodNumber>;
|
|
766
|
+
accept: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
767
|
+
maxSize: z.ZodOptional<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>>;
|
|
589
768
|
}, z.core.$strict>]>>>;
|
|
590
769
|
returns: z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
|
|
591
770
|
name: z.ZodString;
|
|
@@ -601,11 +780,30 @@ export declare const machineSpecSchema: z.ZodObject<{
|
|
|
601
780
|
array: "array";
|
|
602
781
|
}>>;
|
|
603
782
|
description: z.ZodOptional<z.ZodString>;
|
|
783
|
+
format: z.ZodOptional<z.ZodEnum<{
|
|
784
|
+
email: "email";
|
|
785
|
+
url: "url";
|
|
786
|
+
phone: "phone";
|
|
787
|
+
}>>;
|
|
788
|
+
pattern: z.ZodOptional<z.ZodString>;
|
|
789
|
+
minLength: z.ZodOptional<z.ZodNumber>;
|
|
790
|
+
maxLength: z.ZodOptional<z.ZodNumber>;
|
|
791
|
+
enum: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
792
|
+
content: z.ZodOptional<z.ZodEnum<{
|
|
793
|
+
text: "text";
|
|
794
|
+
markdown: "markdown";
|
|
795
|
+
html: "html";
|
|
796
|
+
}>>;
|
|
797
|
+
minimum: z.ZodOptional<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>>;
|
|
798
|
+
maximum: z.ZodOptional<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>>;
|
|
799
|
+
minItems: z.ZodOptional<z.ZodNumber>;
|
|
800
|
+
maxItems: z.ZodOptional<z.ZodNumber>;
|
|
801
|
+
accept: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
802
|
+
maxSize: z.ZodOptional<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>>;
|
|
604
803
|
}, z.core.$strict>]>>>;
|
|
605
804
|
}, z.core.$loose>>>>;
|
|
606
805
|
script: z.ZodOptional<z.ZodString>;
|
|
607
806
|
budget: z.ZodOptional<z.ZodObject<{
|
|
608
|
-
maxTurns: z.ZodOptional<z.ZodNumber>;
|
|
609
807
|
timeoutMs: z.ZodOptional<z.ZodNumber>;
|
|
610
808
|
maxParks: z.ZodOptional<z.ZodNumber>;
|
|
611
809
|
}, z.core.$strict>>;
|
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
import { z } from "zod";
|
|
10
10
|
import { normalizeAllowEntry } from "./allow.js";
|
|
11
11
|
import { isSlug } from "./slug.js";
|
|
12
|
-
import { SIGNATURE_TYPES } from "./signature.js";
|
|
12
|
+
import { boundsInOrder, isBound, isCount, parseSize, RULE_TYPES, SIGNATURE_CONTENTS, SIGNATURE_FORMATS, SIGNATURE_RULE_NAMES, SIGNATURE_TYPES, } from "./signature.js";
|
|
13
|
+
import { compilePattern, jsonSchemaPatternError } from "./signature-pattern.js";
|
|
13
14
|
import { MANUAL_TRIGGER, parseSessionPath, stateTriggerIds } from "./triggers.js";
|
|
14
15
|
import { parseReferences, referenceError, TITLE_VARIABLE, TRIGGER_VARIABLE, VARIABLE_NAME_PATTERN, } from "./variables.js";
|
|
15
16
|
/**
|
|
@@ -156,41 +157,16 @@ export const transitionTypeSchema = z.enum(["approve", "reject", "refine", "none
|
|
|
156
157
|
});
|
|
157
158
|
/**
|
|
158
159
|
* The agent is disclosed the active state's edges and nothing else of the graph,
|
|
159
|
-
* so an edge
|
|
160
|
-
*
|
|
161
|
-
*
|
|
162
|
-
*
|
|
163
|
-
*
|
|
164
|
-
*
|
|
165
|
-
* is the person's button label, and an undescribed edge into a grader or a code
|
|
166
|
-
* state: a grader's instructions are its criteria, which the work it judges is
|
|
167
|
-
* never shown, and a code state has none. A code state's own edges are exempt —
|
|
168
|
-
* its script routes, and nothing reads their descriptions. A non-string is a
|
|
169
|
-
* typo and refused here. The issue path
|
|
160
|
+
* so an edge's `description` is the only thing it is told about the edge — no
|
|
161
|
+
* attribute of the target state is disclosed — and at a human state it is also
|
|
162
|
+
* the decision's button label. A non-string is a typo and refused here; an
|
|
163
|
+
* absent or blank one is an **undescribed** edge, which {@link refineSpec}
|
|
164
|
+
* accepts only where there is nothing to choose and no condition to say (see
|
|
165
|
+
* {@link undescribedEdgeIssue}). The issue path
|
|
170
166
|
* (`states.<slug>.transitions.<index>.description`) names the edge.
|
|
171
167
|
*/
|
|
172
|
-
const TRANSITION_DESCRIPTION = `must be a string:
|
|
173
|
-
`(e.g. 'Refunds over $50'), not where it goes
|
|
174
|
-
`instructions instead.`;
|
|
175
|
-
/** An undescribed edge whose target has no instructions: nothing to route on. */
|
|
176
|
-
function undescribedEdgeMessage(target) {
|
|
177
|
-
return (`must be a non-empty string here: the agent routes on an edge's description, or, when it has ` +
|
|
178
|
-
`none, on the target state's instructions — and '${target}' declares none. Say when to take ` +
|
|
179
|
-
`this edge (e.g. 'Refunds over $50'), or give '${target}' instructions.`);
|
|
180
|
-
}
|
|
181
|
-
/** An undescribed edge into an automated state: there are no instructions the agent may be shown. */
|
|
182
|
-
function undescribedAutomatedEdgeMessage(target, type) {
|
|
183
|
-
const why = type === "grader"
|
|
184
|
-
? `'${target}' is a grader, whose instructions are its criteria and never shown to the work it judges`
|
|
185
|
-
: `'${target}' is a code state, which has none`;
|
|
186
|
-
return (`must be a non-empty string here: the agent routes on an edge's description, or, when it has ` +
|
|
187
|
-
`none, on the target state's instructions — and ${why}. Say when to take this edge ` +
|
|
188
|
-
`(e.g. 'The reply is drafted').`);
|
|
189
|
-
}
|
|
190
|
-
/** An undescribed transition on a human state: the decision's button has no label. */
|
|
191
|
-
const HUMAN_TRANSITION_DESCRIPTION = `must be a non-empty string on a human state's transition: it is the label of the decision's ` +
|
|
192
|
-
`button for the person deciding, and a target's instructions do not tell them what choosing it ` +
|
|
193
|
-
`means. Say what the choice is (e.g. 'Approve the refund').`;
|
|
168
|
+
const TRANSITION_DESCRIPTION = `must be a string: it is the only thing the agent is told about this edge, so say when to ` +
|
|
169
|
+
`take it (e.g. 'Refunds over $50'), not where it goes.`;
|
|
194
170
|
/** @see MachineTransition */
|
|
195
171
|
export const machineTransitionSchema = z
|
|
196
172
|
.object({
|
|
@@ -199,14 +175,13 @@ export const machineTransitionSchema = z
|
|
|
199
175
|
type: transitionTypeSchema.optional(),
|
|
200
176
|
})
|
|
201
177
|
.strict();
|
|
202
|
-
/** Whether an edge carries no description
|
|
178
|
+
/** Whether an edge carries no description: absent, empty or blank. */
|
|
203
179
|
export function isUndescribed(transition) {
|
|
204
180
|
return !transition.description?.trim();
|
|
205
181
|
}
|
|
206
182
|
/** @see StateBudget */
|
|
207
183
|
export const stateBudgetSchema = z
|
|
208
184
|
.object({
|
|
209
|
-
maxTurns: positiveInt.optional(),
|
|
210
185
|
timeoutMs: positiveInt.optional(),
|
|
211
186
|
maxParks: positiveInt.optional(),
|
|
212
187
|
})
|
|
@@ -300,15 +275,39 @@ export const stateTypeSchema = z.enum(["agent", "human", "grader", "code"], {
|
|
|
300
275
|
});
|
|
301
276
|
// --- Triggers ------------------------------------------------------------------
|
|
302
277
|
/** The keys a signature entry object may carry, as a refusal lists them. */
|
|
303
|
-
const SIGNATURE_ENTRY_KEYS =
|
|
278
|
+
const SIGNATURE_ENTRY_KEYS = `'name', 'type', 'description' and the rules ` +
|
|
279
|
+
`${SIGNATURE_RULE_NAMES.slice(0, -1).map((rule) => `'${rule}'`).join(", ")} and '${SIGNATURE_RULE_NAMES.at(-1)}'`;
|
|
304
280
|
/** The accepted type words, as a refusal lists them. */
|
|
305
281
|
const SIGNATURE_TYPE_LIST = `${SIGNATURE_TYPES.slice(0, -1).join(", ")} or ${SIGNATURE_TYPES.at(-1)}`;
|
|
282
|
+
/** What a `maxSize` must look like, as a refusal says it. */
|
|
283
|
+
const SIZE_SHAPE = "a size in bytes: an integer, or '<n>KB' / '<n>MB' (e.g. 10MB)";
|
|
284
|
+
/** One file extension as `accept` lists it: a dot and a name holding no further dot or separator. */
|
|
285
|
+
const EXTENSION = /^\.[^./\\]+$/;
|
|
286
|
+
/** A length or count rule's shape; that it is a non-negative integer is the refinement's. */
|
|
287
|
+
function countRule(rule) {
|
|
288
|
+
return z.number({ error: `'${rule}' must be a non-negative integer` }).optional();
|
|
289
|
+
}
|
|
290
|
+
/** A `minimum`/`maximum` rule's shape; which of the two kinds the entry's type takes is the refinement's. */
|
|
291
|
+
function boundRule(rule) {
|
|
292
|
+
return z
|
|
293
|
+
.union([z.number(), z.string()], {
|
|
294
|
+
error: `'${rule}' must be a number, a date (YYYY-MM-DD or 'today') or a date-time (RFC 3339 or 'now')`,
|
|
295
|
+
})
|
|
296
|
+
.optional();
|
|
297
|
+
}
|
|
298
|
+
/** A list rule's shape: strings, with emptiness and repeats left to the refinement. */
|
|
299
|
+
function listRule(rule) {
|
|
300
|
+
return z
|
|
301
|
+
.array(z.string({ error: `'${rule}' must list strings` }), { error: `'${rule}' must be a list of strings` })
|
|
302
|
+
.optional();
|
|
303
|
+
}
|
|
306
304
|
/**
|
|
307
305
|
* @see SignatureEntryDeclaration
|
|
308
306
|
*
|
|
309
307
|
* The object spelling of a signature entry. Strict, unlike the loose declaration
|
|
310
308
|
* around it: an unknown key here is most likely a misspelled `type` or
|
|
311
|
-
* `description`, and read loosely it would silently mean "untyped".
|
|
309
|
+
* `description`, and read loosely it would silently mean "untyped". A rule key
|
|
310
|
+
* is shape-checked here and held to its entry's type by {@link refineRules}.
|
|
312
311
|
*/
|
|
313
312
|
export const signatureEntryObjectSchema = z.strictObject({
|
|
314
313
|
name: z
|
|
@@ -328,12 +327,126 @@ export const signatureEntryObjectSchema = z.strictObject({
|
|
|
328
327
|
.string({ error: `'description' ${NON_EMPTY}` })
|
|
329
328
|
.refine((value) => value.trim() !== "", `'description' ${NON_EMPTY}`)
|
|
330
329
|
.optional(),
|
|
330
|
+
format: z
|
|
331
|
+
.enum(SIGNATURE_FORMATS, {
|
|
332
|
+
error: (issue) => `'format' must be one of email, url or phone; got ${JSON.stringify(issue.input)}`,
|
|
333
|
+
})
|
|
334
|
+
.optional(),
|
|
335
|
+
pattern: z.string({ error: "'pattern' must be a string (a regular expression)" }).optional(),
|
|
336
|
+
minLength: countRule("minLength"),
|
|
337
|
+
maxLength: countRule("maxLength"),
|
|
338
|
+
enum: listRule("enum"),
|
|
339
|
+
content: z
|
|
340
|
+
.enum(SIGNATURE_CONTENTS, {
|
|
341
|
+
error: (issue) => `'content' must be one of text, markdown or html; got ${JSON.stringify(issue.input)}`,
|
|
342
|
+
})
|
|
343
|
+
.optional(),
|
|
344
|
+
minimum: boundRule("minimum"),
|
|
345
|
+
maximum: boundRule("maximum"),
|
|
346
|
+
minItems: countRule("minItems"),
|
|
347
|
+
maxItems: countRule("maxItems"),
|
|
348
|
+
accept: listRule("accept"),
|
|
349
|
+
maxSize: z.union([z.number(), z.string()], { error: `'maxSize' must be ${SIZE_SHAPE}` }).optional(),
|
|
331
350
|
}, {
|
|
332
351
|
error: (issue) => issue.code === "unrecognized_keys"
|
|
333
352
|
? `${entryLabel(issue.input)} declares ${issue.keys.map((k) => `'${k}'`).join(", ")}, which a ` +
|
|
334
353
|
`signature entry does not take: it has ${SIGNATURE_ENTRY_KEYS} only.`
|
|
335
354
|
: undefined,
|
|
336
|
-
});
|
|
355
|
+
}).superRefine(refineRules);
|
|
356
|
+
/** `string`, `integer, number, date and date-time`. */
|
|
357
|
+
function typeList(types) {
|
|
358
|
+
return types.length === 1 ? types[0] : `${types.slice(0, -1).join(", ")} and ${types.at(-1)}`;
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
* Hold an entry's rules to its type and to each other. Each issue sits at the
|
|
362
|
+
* rule's key and names the entry itself: the union around the entry hands a
|
|
363
|
+
* refinement's issues on unwrapped.
|
|
364
|
+
*/
|
|
365
|
+
function refineRules(entry, ctx) {
|
|
366
|
+
const declared = SIGNATURE_RULE_NAMES.filter((rule) => entry[rule] !== undefined);
|
|
367
|
+
if (declared.length === 0)
|
|
368
|
+
return;
|
|
369
|
+
const label = entryLabel(entry);
|
|
370
|
+
const issue = (rule, message) => ctx.addIssue({ code: "custom", path: [rule], message: `${label} ${message}` });
|
|
371
|
+
const { type } = entry;
|
|
372
|
+
const applicable = [];
|
|
373
|
+
for (const rule of declared) {
|
|
374
|
+
const types = RULE_TYPES[rule];
|
|
375
|
+
if (!type)
|
|
376
|
+
issue(rule, `declares '${rule}', but has no 'type': it applies to ${typeList(types)} entries only.`);
|
|
377
|
+
else if (!types.includes(type))
|
|
378
|
+
issue(rule, `declares '${rule}', which applies to ${typeList(types)} entries only.`);
|
|
379
|
+
else
|
|
380
|
+
applicable.push(rule);
|
|
381
|
+
}
|
|
382
|
+
if (!type)
|
|
383
|
+
return;
|
|
384
|
+
const has = (rule) => applicable.includes(rule);
|
|
385
|
+
for (const rule of ["minLength", "maxLength", "minItems", "maxItems"]) {
|
|
386
|
+
if (has(rule) && !isCount(entry[rule])) {
|
|
387
|
+
issue(rule, `declares '${rule}' as ${String(entry[rule])}, which is not a non-negative integer.`);
|
|
388
|
+
applicable.splice(applicable.indexOf(rule), 1);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
if (has("format") && has("pattern")) {
|
|
392
|
+
issue("pattern", `declares both 'format' and 'pattern': an entry declares one or the other.`);
|
|
393
|
+
}
|
|
394
|
+
if (has("pattern")) {
|
|
395
|
+
const pattern = entry.pattern;
|
|
396
|
+
const compiled = compilePattern(pattern);
|
|
397
|
+
const error = compiled.ok ? jsonSchemaPatternError(pattern) : compiled.error;
|
|
398
|
+
if (error)
|
|
399
|
+
issue("pattern", `declares a 'pattern' that cannot be used: ${error}.`);
|
|
400
|
+
}
|
|
401
|
+
for (const rule of ["minimum", "maximum"]) {
|
|
402
|
+
if (!has(rule) || isBound(type, entry[rule]))
|
|
403
|
+
continue;
|
|
404
|
+
const shape = type === "date"
|
|
405
|
+
? "a date (YYYY-MM-DD, a real calendar day) or 'today'"
|
|
406
|
+
: type === "date-time"
|
|
407
|
+
? "an RFC 3339 date-time with an offset (e.g. 2026-11-01T09:00:00Z) or 'now'"
|
|
408
|
+
: "a number";
|
|
409
|
+
issue(rule, `declares '${rule}' as ${JSON.stringify(entry[rule])}, but on a ${type} entry it must be ${shape}.`);
|
|
410
|
+
}
|
|
411
|
+
for (const [low, high] of [
|
|
412
|
+
["minLength", "maxLength"],
|
|
413
|
+
["minimum", "maximum"],
|
|
414
|
+
["minItems", "maxItems"],
|
|
415
|
+
]) {
|
|
416
|
+
if (!has(low) || !has(high))
|
|
417
|
+
continue;
|
|
418
|
+
const ordered = low === "minimum"
|
|
419
|
+
? boundsInOrder(type, entry[low], entry[high])
|
|
420
|
+
: entry[low] <= entry[high];
|
|
421
|
+
if (!ordered) {
|
|
422
|
+
issue(low, `declares '${low}' ${JSON.stringify(entry[low])} above '${high}' ${JSON.stringify(entry[high])}, ` +
|
|
423
|
+
`so no value could conform.`);
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
for (const rule of ["enum", "accept"]) {
|
|
427
|
+
if (!has(rule))
|
|
428
|
+
continue;
|
|
429
|
+
const list = entry[rule];
|
|
430
|
+
if (list.length === 0) {
|
|
431
|
+
issue(rule, `declares an empty '${rule}', so no value could conform.`);
|
|
432
|
+
continue;
|
|
433
|
+
}
|
|
434
|
+
const malformed = rule === "accept" ? list.find((extension) => !EXTENSION.test(extension)) : undefined;
|
|
435
|
+
if (malformed !== undefined) {
|
|
436
|
+
issue(rule, `lists ${JSON.stringify(malformed)} in 'accept', which is not a file extension: write a dot and ` +
|
|
437
|
+
`a name with no further dot, e.g. '.pdf'.`);
|
|
438
|
+
continue;
|
|
439
|
+
}
|
|
440
|
+
// Extensions compare case-insensitively, as `accept` is checked.
|
|
441
|
+
const keys = list.map((item) => (rule === "accept" ? item.toLowerCase() : item));
|
|
442
|
+
const repeated = list.find((_, index) => keys.indexOf(keys[index]) !== index);
|
|
443
|
+
if (repeated !== undefined)
|
|
444
|
+
issue(rule, `lists ${JSON.stringify(repeated)} more than once in '${rule}'.`);
|
|
445
|
+
}
|
|
446
|
+
if (has("maxSize") && parseSize(entry.maxSize) === undefined) {
|
|
447
|
+
issue("maxSize", `declares 'maxSize' as ${JSON.stringify(entry.maxSize)}, which is not ${SIZE_SHAPE}.`);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
337
450
|
/** An entry as a message names it: by its `name` when it has a readable one. */
|
|
338
451
|
function entryLabel(input) {
|
|
339
452
|
const name = input?.name;
|
|
@@ -659,6 +772,59 @@ function lockIssues(states) {
|
|
|
659
772
|
}
|
|
660
773
|
return issues;
|
|
661
774
|
}
|
|
775
|
+
/** The type a state declares, `agent` when it declares none. */
|
|
776
|
+
function stateTypeOf(state) {
|
|
777
|
+
return state?.type ?? "agent";
|
|
778
|
+
}
|
|
779
|
+
/** Why an edge of a state that is not an agent state needs a description, and what to write. */
|
|
780
|
+
const NON_AGENT_EDGE = {
|
|
781
|
+
human: (slug) => `'${slug}' is a human state, where an edge's description is the label of the person's ` +
|
|
782
|
+
`button. Say what choosing it means (e.g. 'Approve the refund').`,
|
|
783
|
+
grader: (slug) => `'${slug}' is a grader state, which chooses its edge by the descriptions. Say when to take ` +
|
|
784
|
+
`it (e.g. 'The email is ready to send').`,
|
|
785
|
+
code: (slug) => `'${slug}' is a code state: its script routes, and the description is what a reader of the ` +
|
|
786
|
+
`spec and the trail goes by. Say when the script takes it (e.g. 'The decision matches the policy').`,
|
|
787
|
+
};
|
|
788
|
+
/** What an edge into a state that is not an agent state hands the session to. */
|
|
789
|
+
const HANDED_TO = {
|
|
790
|
+
human: "a person's decision",
|
|
791
|
+
grader: "a grader's check",
|
|
792
|
+
code: "a code state's check",
|
|
793
|
+
};
|
|
794
|
+
/**
|
|
795
|
+
* Why `slug`'s undescribed edge at `index` is refused, or `undefined` when it
|
|
796
|
+
* stands. An edge may go undescribed only where the agent has nothing to choose
|
|
797
|
+
* and nothing to wait for beyond the state's own work: it is the **only** edge
|
|
798
|
+
* of an **agent** state that has `instructions`, and it leads into **another
|
|
799
|
+
* agent** state. The agent takes it as soon as those instructions are done, and
|
|
800
|
+
* is told so in the edge's place. Every refusal says what to write instead.
|
|
801
|
+
*/
|
|
802
|
+
function undescribedEdgeIssue(states, slug, index) {
|
|
803
|
+
const state = states[slug];
|
|
804
|
+
const transitions = state.transitions ?? [];
|
|
805
|
+
const to = transitions[index].to;
|
|
806
|
+
const type = stateTypeOf(state);
|
|
807
|
+
const target = stateTypeOf(states[to]);
|
|
808
|
+
const refuse = (why) => `must be a non-empty string here: ${why}`;
|
|
809
|
+
const say = `Say when to take it (e.g. 'The customer confirmed the address').`;
|
|
810
|
+
if (type !== "agent")
|
|
811
|
+
return refuse(NON_AGENT_EDGE[type](slug));
|
|
812
|
+
if (transitions.length > 1) {
|
|
813
|
+
return refuse(`'${slug}' has ${transitions.length} transitions, and the agent chooses between them by their ` +
|
|
814
|
+
`descriptions; only a state's single transition may go without one. ${say}`);
|
|
815
|
+
}
|
|
816
|
+
if (to === slug)
|
|
817
|
+
return refuse(`this edge leads back into '${slug}' itself, so it needs a condition. ${say}`);
|
|
818
|
+
if (target !== "agent") {
|
|
819
|
+
return refuse(`'${to}' is a ${target} state, so this edge hands the session to ${HANDED_TO[target]}. Say ` +
|
|
820
|
+
`what must be ready for it (e.g. 'The reply is drafted').`);
|
|
821
|
+
}
|
|
822
|
+
if (!state.instructions?.trim()) {
|
|
823
|
+
return refuse(`'${slug}' declares no instructions, and an undescribed edge is taken once they are done. ` +
|
|
824
|
+
`Give '${slug}' instructions, or say when to take this edge.`);
|
|
825
|
+
}
|
|
826
|
+
return undefined;
|
|
827
|
+
}
|
|
662
828
|
export function refineSpec(spec) {
|
|
663
829
|
const issues = [];
|
|
664
830
|
const issue = (path, message) => issues.push({ path, message });
|
|
@@ -675,19 +841,11 @@ export function refineSpec(spec) {
|
|
|
675
841
|
issue(`${at}.transitions.${index}.to`, `State '${slug}' transitions to undefined state '${transition.to}'`);
|
|
676
842
|
return;
|
|
677
843
|
}
|
|
678
|
-
|
|
679
|
-
if (!isUndescribed(transition) || state.type === "code")
|
|
844
|
+
if (!isUndescribed(transition))
|
|
680
845
|
return;
|
|
681
|
-
const
|
|
682
|
-
if (
|
|
683
|
-
issue(`${at}.transitions.${index}.description`,
|
|
684
|
-
}
|
|
685
|
-
else if (target?.type === "grader" || target?.type === "code") {
|
|
686
|
-
issue(`${at}.transitions.${index}.description`, undescribedAutomatedEdgeMessage(transition.to, target.type));
|
|
687
|
-
}
|
|
688
|
-
else if (!target?.instructions?.trim()) {
|
|
689
|
-
issue(`${at}.transitions.${index}.description`, undescribedEdgeMessage(transition.to));
|
|
690
|
-
}
|
|
846
|
+
const refusal = undescribedEdgeIssue(states, slug, index);
|
|
847
|
+
if (refusal)
|
|
848
|
+
issue(`${at}.transitions.${index}.description`, refusal);
|
|
691
849
|
});
|
|
692
850
|
if (state.on_error !== undefined && !(state.on_error in states)) {
|
|
693
851
|
issue(`${at}.on_error`, `State '${slug}' on_error routes to undefined state '${state.on_error}'`);
|