markform 0.1.3 → 0.1.5
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 +110 -70
- package/dist/ai-sdk.d.mts +2 -2
- package/dist/ai-sdk.mjs +5 -5
- package/dist/{apply-00UmzDKL.mjs → apply-BCCiJzQr.mjs} +371 -26
- package/dist/bin.mjs +6 -6
- package/dist/{cli-D--Lel-e.mjs → cli-D469amuk.mjs} +386 -96
- package/dist/cli.mjs +6 -6
- package/dist/{coreTypes-BXhhz9Iq.d.mts → coreTypes-9XZSNOv6.d.mts} +1878 -325
- package/dist/{coreTypes-Dful87E0.mjs → coreTypes-pyctKRgc.mjs} +79 -5
- package/dist/index.d.mts +142 -5
- package/dist/index.mjs +5 -5
- package/dist/session-B_stoXQn.mjs +4 -0
- package/dist/{session-Bqnwi9wp.mjs → session-uF0e6m6k.mjs} +9 -5
- package/dist/{shared-N_s1M-_K.mjs → shared-BqPnYXrn.mjs} +82 -1
- package/dist/shared-CZsyShck.mjs +3 -0
- package/dist/{src-Dm8jZ5dl.mjs → src-Df0XX7UB.mjs} +818 -125
- package/docs/markform-apis.md +194 -0
- package/{DOCS.md → docs/markform-reference.md} +130 -69
- package/{SPEC.md → docs/markform-spec.md} +359 -108
- package/examples/earnings-analysis/earnings-analysis.form.md +88 -800
- package/examples/earnings-analysis/earnings-analysis.valid.ts +16 -148
- package/examples/movie-research/movie-research-basic.form.md +41 -37
- package/examples/movie-research/movie-research-deep.form.md +110 -98
- package/examples/movie-research/movie-research-minimal.form.md +29 -15
- package/examples/simple/simple-mock-filled.form.md +105 -41
- package/examples/simple/simple-skipped-filled.form.md +103 -41
- package/examples/simple/simple-with-skips.session.yaml +93 -25
- package/examples/simple/simple.form.md +86 -32
- package/examples/simple/simple.session.yaml +98 -25
- package/examples/startup-deep-research/startup-deep-research.form.md +130 -103
- package/examples/startup-research/startup-research-mock-filled.form.md +55 -55
- package/examples/startup-research/startup-research.form.md +36 -36
- package/package.json +18 -19
- package/dist/session-DdAtY2Ni.mjs +0 -4
- package/dist/shared-D7gf27Tr.mjs +0 -3
- package/examples/celebrity-deep-research/celebrity-deep-research.form.md +0 -912
|
@@ -2,11 +2,33 @@ import { resolve } from "node:path";
|
|
|
2
2
|
|
|
3
3
|
//#region src/llms.ts
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
5
|
+
* Parse a model ID string into provider and model components for display.
|
|
6
6
|
*
|
|
7
|
-
* This
|
|
8
|
-
*
|
|
9
|
-
|
|
7
|
+
* This is a non-throwing utility for extracting display-friendly components
|
|
8
|
+
* from a model ID. For validation and resolution, use `parseModelId` from
|
|
9
|
+
* `modelResolver.ts` instead.
|
|
10
|
+
*
|
|
11
|
+
* @param modelId - Model ID in format `provider/model-id`
|
|
12
|
+
* @returns Parsed components with 'unknown' provider if format is invalid
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* parseModelIdForDisplay('anthropic/claude-sonnet-4')
|
|
16
|
+
* // => { provider: 'anthropic', model: 'claude-sonnet-4' }
|
|
17
|
+
*
|
|
18
|
+
* parseModelIdForDisplay('claude-sonnet-4')
|
|
19
|
+
* // => { provider: 'unknown', model: 'claude-sonnet-4' }
|
|
20
|
+
*/
|
|
21
|
+
function parseModelIdForDisplay(modelId) {
|
|
22
|
+
const slashIndex = modelId.indexOf("/");
|
|
23
|
+
if (slashIndex === -1 || slashIndex === 0 || slashIndex === modelId.length - 1) return {
|
|
24
|
+
provider: "unknown",
|
|
25
|
+
model: modelId
|
|
26
|
+
};
|
|
27
|
+
return {
|
|
28
|
+
provider: modelId.slice(0, slashIndex),
|
|
29
|
+
model: modelId.slice(slashIndex + 1)
|
|
30
|
+
};
|
|
31
|
+
}
|
|
10
32
|
/**
|
|
11
33
|
* Suggested LLM models for the fill command, organized by provider.
|
|
12
34
|
* These are shown in help/error messages and model selection prompts.
|
|
@@ -246,6 +268,35 @@ function deriveReportPath(basePath) {
|
|
|
246
268
|
return base + REPORT_EXTENSION;
|
|
247
269
|
}
|
|
248
270
|
|
|
271
|
+
//#endregion
|
|
272
|
+
//#region src/utils/keySort.ts
|
|
273
|
+
/**
|
|
274
|
+
* Key-based sorting utilities.
|
|
275
|
+
*/
|
|
276
|
+
/** Create a comparator from a key function. */
|
|
277
|
+
function keyComparator(keyFn) {
|
|
278
|
+
return (a, b) => {
|
|
279
|
+
const ka = keyFn(a), kb = keyFn(b);
|
|
280
|
+
return ka < kb ? -1 : ka > kb ? 1 : 0;
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Comparator that sorts priority keys first (in order), then remaining keys alphabetically.
|
|
285
|
+
*
|
|
286
|
+
* @example
|
|
287
|
+
* ```ts
|
|
288
|
+
* const attrs = { label: 'Name', kind: 'string', id: 'name', required: true };
|
|
289
|
+
* const keys = Object.keys(attrs).sort(priorityKeyComparator(['kind', 'id']));
|
|
290
|
+
* // => ['kind', 'id', 'label', 'required']
|
|
291
|
+
* ```
|
|
292
|
+
*/
|
|
293
|
+
function priorityKeyComparator(priorityKeys) {
|
|
294
|
+
return keyComparator((key) => {
|
|
295
|
+
const i = priorityKeys.indexOf(key);
|
|
296
|
+
return [i !== -1 ? i : Infinity, key];
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
|
|
249
300
|
//#endregion
|
|
250
301
|
//#region src/engine/serialize.ts
|
|
251
302
|
/**
|
|
@@ -320,11 +371,18 @@ function serializeAttrValue(value) {
|
|
|
320
371
|
if (typeof value === "undefined") return "null";
|
|
321
372
|
throw new Error(`Cannot serialize value of type ${typeof value} to Markdoc`);
|
|
322
373
|
}
|
|
374
|
+
/** Priority keys that appear first in serialized attributes, in this order. */
|
|
375
|
+
const ATTR_PRIORITY_KEYS = [
|
|
376
|
+
"kind",
|
|
377
|
+
"id",
|
|
378
|
+
"role"
|
|
379
|
+
];
|
|
323
380
|
/**
|
|
324
|
-
* Serialize attributes to Markdoc format
|
|
381
|
+
* Serialize attributes to Markdoc format.
|
|
382
|
+
* Priority keys (kind, id, role) appear first in order, then remaining keys alphabetically.
|
|
325
383
|
*/
|
|
326
384
|
function serializeAttrs(attrs) {
|
|
327
|
-
const keys = Object.keys(attrs).sort();
|
|
385
|
+
const keys = Object.keys(attrs).sort(priorityKeyComparator(ATTR_PRIORITY_KEYS));
|
|
328
386
|
const parts = [];
|
|
329
387
|
for (const key of keys) {
|
|
330
388
|
const value = attrs[key];
|
|
@@ -354,6 +412,7 @@ function getMarker(state) {
|
|
|
354
412
|
*/
|
|
355
413
|
function serializeStringField(field, response) {
|
|
356
414
|
const attrs = {
|
|
415
|
+
kind: "string",
|
|
357
416
|
id: field.id,
|
|
358
417
|
label: field.label
|
|
359
418
|
};
|
|
@@ -366,6 +425,8 @@ function serializeStringField(field, response) {
|
|
|
366
425
|
if (field.maxLength !== void 0) attrs.maxLength = field.maxLength;
|
|
367
426
|
if (field.validate) attrs.validate = field.validate;
|
|
368
427
|
if (field.report !== void 0) attrs.report = field.report;
|
|
428
|
+
if (field.placeholder) attrs.placeholder = field.placeholder;
|
|
429
|
+
if (field.examples && field.examples.length > 0) attrs.examples = field.examples;
|
|
369
430
|
if (response?.state === "skipped" || response?.state === "aborted") attrs.state = response.state;
|
|
370
431
|
const attrStr = serializeAttrs(attrs);
|
|
371
432
|
let content = "";
|
|
@@ -375,13 +436,14 @@ function serializeStringField(field, response) {
|
|
|
375
436
|
}
|
|
376
437
|
const sentinelContent = getSentinelContent(response);
|
|
377
438
|
if (sentinelContent) content = sentinelContent;
|
|
378
|
-
return `{%
|
|
439
|
+
return `{% field ${attrStr} %}${content}{% /field %}`;
|
|
379
440
|
}
|
|
380
441
|
/**
|
|
381
442
|
* Serialize a number field.
|
|
382
443
|
*/
|
|
383
444
|
function serializeNumberField(field, response) {
|
|
384
445
|
const attrs = {
|
|
446
|
+
kind: "number",
|
|
385
447
|
id: field.id,
|
|
386
448
|
label: field.label
|
|
387
449
|
};
|
|
@@ -393,6 +455,8 @@ function serializeNumberField(field, response) {
|
|
|
393
455
|
if (field.integer) attrs.integer = field.integer;
|
|
394
456
|
if (field.validate) attrs.validate = field.validate;
|
|
395
457
|
if (field.report !== void 0) attrs.report = field.report;
|
|
458
|
+
if (field.placeholder) attrs.placeholder = field.placeholder;
|
|
459
|
+
if (field.examples && field.examples.length > 0) attrs.examples = field.examples;
|
|
396
460
|
if (response?.state === "skipped" || response?.state === "aborted") attrs.state = response.state;
|
|
397
461
|
const attrStr = serializeAttrs(attrs);
|
|
398
462
|
let content = "";
|
|
@@ -402,13 +466,14 @@ function serializeNumberField(field, response) {
|
|
|
402
466
|
}
|
|
403
467
|
const sentinelContent = getSentinelContent(response);
|
|
404
468
|
if (sentinelContent) content = sentinelContent;
|
|
405
|
-
return `{%
|
|
469
|
+
return `{% field ${attrStr} %}${content}{% /field %}`;
|
|
406
470
|
}
|
|
407
471
|
/**
|
|
408
472
|
* Serialize a string-list field.
|
|
409
473
|
*/
|
|
410
474
|
function serializeStringListField(field, response) {
|
|
411
475
|
const attrs = {
|
|
476
|
+
kind: "string_list",
|
|
412
477
|
id: field.id,
|
|
413
478
|
label: field.label
|
|
414
479
|
};
|
|
@@ -422,6 +487,8 @@ function serializeStringListField(field, response) {
|
|
|
422
487
|
if (field.uniqueItems) attrs.uniqueItems = field.uniqueItems;
|
|
423
488
|
if (field.validate) attrs.validate = field.validate;
|
|
424
489
|
if (field.report !== void 0) attrs.report = field.report;
|
|
490
|
+
if (field.placeholder) attrs.placeholder = field.placeholder;
|
|
491
|
+
if (field.examples && field.examples.length > 0) attrs.examples = field.examples;
|
|
425
492
|
if (response?.state === "skipped" || response?.state === "aborted") attrs.state = response.state;
|
|
426
493
|
const attrStr = serializeAttrs(attrs);
|
|
427
494
|
let content = "";
|
|
@@ -431,7 +498,7 @@ function serializeStringListField(field, response) {
|
|
|
431
498
|
}
|
|
432
499
|
const sentinelContent = getSentinelContent(response);
|
|
433
500
|
if (sentinelContent) content = sentinelContent;
|
|
434
|
-
return `{%
|
|
501
|
+
return `{% field ${attrStr} %}${content}{% /field %}`;
|
|
435
502
|
}
|
|
436
503
|
/**
|
|
437
504
|
* Serialize options (for single-select, multi-select, checkboxes).
|
|
@@ -449,6 +516,7 @@ function serializeOptions(options, selected) {
|
|
|
449
516
|
*/
|
|
450
517
|
function serializeSingleSelectField(field, response) {
|
|
451
518
|
const attrs = {
|
|
519
|
+
kind: "single_select",
|
|
452
520
|
id: field.id,
|
|
453
521
|
label: field.label
|
|
454
522
|
};
|
|
@@ -463,13 +531,14 @@ function serializeSingleSelectField(field, response) {
|
|
|
463
531
|
if (response?.state === "answered" && response.value) value = response.value;
|
|
464
532
|
const selected = {};
|
|
465
533
|
for (const opt of field.options) selected[opt.id] = opt.id === value?.selected ? "done" : "todo";
|
|
466
|
-
return `{%
|
|
534
|
+
return `{% field ${attrStr} %}\n${serializeOptions(field.options, selected)}\n{% /field %}`;
|
|
467
535
|
}
|
|
468
536
|
/**
|
|
469
537
|
* Serialize a multi-select field.
|
|
470
538
|
*/
|
|
471
539
|
function serializeMultiSelectField(field, response) {
|
|
472
540
|
const attrs = {
|
|
541
|
+
kind: "multi_select",
|
|
473
542
|
id: field.id,
|
|
474
543
|
label: field.label
|
|
475
544
|
};
|
|
@@ -487,13 +556,14 @@ function serializeMultiSelectField(field, response) {
|
|
|
487
556
|
const selected = {};
|
|
488
557
|
const selectedSet = new Set(value?.selected ?? []);
|
|
489
558
|
for (const opt of field.options) selected[opt.id] = selectedSet.has(opt.id) ? "done" : "todo";
|
|
490
|
-
return `{%
|
|
559
|
+
return `{% field ${attrStr} %}\n${serializeOptions(field.options, selected)}\n{% /field %}`;
|
|
491
560
|
}
|
|
492
561
|
/**
|
|
493
562
|
* Serialize a checkboxes field.
|
|
494
563
|
*/
|
|
495
564
|
function serializeCheckboxesField(field, response) {
|
|
496
565
|
const attrs = {
|
|
566
|
+
kind: "checkboxes",
|
|
497
567
|
id: field.id,
|
|
498
568
|
label: field.label
|
|
499
569
|
};
|
|
@@ -509,13 +579,14 @@ function serializeCheckboxesField(field, response) {
|
|
|
509
579
|
const attrStr = serializeAttrs(attrs);
|
|
510
580
|
let value;
|
|
511
581
|
if (response?.state === "answered" && response.value) value = response.value;
|
|
512
|
-
return `{%
|
|
582
|
+
return `{% field ${attrStr} %}\n${serializeOptions(field.options, value?.values ?? {})}\n{% /field %}`;
|
|
513
583
|
}
|
|
514
584
|
/**
|
|
515
585
|
* Serialize a url-field.
|
|
516
586
|
*/
|
|
517
587
|
function serializeUrlField(field, response) {
|
|
518
588
|
const attrs = {
|
|
589
|
+
kind: "url",
|
|
519
590
|
id: field.id,
|
|
520
591
|
label: field.label
|
|
521
592
|
};
|
|
@@ -524,6 +595,8 @@ function serializeUrlField(field, response) {
|
|
|
524
595
|
if (field.role !== AGENT_ROLE) attrs.role = field.role;
|
|
525
596
|
if (field.validate) attrs.validate = field.validate;
|
|
526
597
|
if (field.report !== void 0) attrs.report = field.report;
|
|
598
|
+
if (field.placeholder) attrs.placeholder = field.placeholder;
|
|
599
|
+
if (field.examples && field.examples.length > 0) attrs.examples = field.examples;
|
|
527
600
|
if (response?.state === "skipped" || response?.state === "aborted") attrs.state = response.state;
|
|
528
601
|
const attrStr = serializeAttrs(attrs);
|
|
529
602
|
let content = "";
|
|
@@ -533,13 +606,14 @@ function serializeUrlField(field, response) {
|
|
|
533
606
|
}
|
|
534
607
|
const sentinelContent = getSentinelContent(response);
|
|
535
608
|
if (sentinelContent) content = sentinelContent;
|
|
536
|
-
return `{%
|
|
609
|
+
return `{% field ${attrStr} %}${content}{% /field %}`;
|
|
537
610
|
}
|
|
538
611
|
/**
|
|
539
612
|
* Serialize a url-list field.
|
|
540
613
|
*/
|
|
541
614
|
function serializeUrlListField(field, response) {
|
|
542
615
|
const attrs = {
|
|
616
|
+
kind: "url_list",
|
|
543
617
|
id: field.id,
|
|
544
618
|
label: field.label
|
|
545
619
|
};
|
|
@@ -551,6 +625,8 @@ function serializeUrlListField(field, response) {
|
|
|
551
625
|
if (field.uniqueItems) attrs.uniqueItems = field.uniqueItems;
|
|
552
626
|
if (field.validate) attrs.validate = field.validate;
|
|
553
627
|
if (field.report !== void 0) attrs.report = field.report;
|
|
628
|
+
if (field.placeholder) attrs.placeholder = field.placeholder;
|
|
629
|
+
if (field.examples && field.examples.length > 0) attrs.examples = field.examples;
|
|
554
630
|
if (response?.state === "skipped" || response?.state === "aborted") attrs.state = response.state;
|
|
555
631
|
const attrStr = serializeAttrs(attrs);
|
|
556
632
|
let content = "";
|
|
@@ -560,13 +636,14 @@ function serializeUrlListField(field, response) {
|
|
|
560
636
|
}
|
|
561
637
|
const sentinelContent = getSentinelContent(response);
|
|
562
638
|
if (sentinelContent) content = sentinelContent;
|
|
563
|
-
return `{%
|
|
639
|
+
return `{% field ${attrStr} %}${content}{% /field %}`;
|
|
564
640
|
}
|
|
565
641
|
/**
|
|
566
642
|
* Serialize a date-field.
|
|
567
643
|
*/
|
|
568
644
|
function serializeDateField(field, response) {
|
|
569
645
|
const attrs = {
|
|
646
|
+
kind: "date",
|
|
570
647
|
id: field.id,
|
|
571
648
|
label: field.label
|
|
572
649
|
};
|
|
@@ -586,13 +663,14 @@ function serializeDateField(field, response) {
|
|
|
586
663
|
}
|
|
587
664
|
const sentinelContent = getSentinelContent(response);
|
|
588
665
|
if (sentinelContent) content = sentinelContent;
|
|
589
|
-
return `{%
|
|
666
|
+
return `{% field ${attrStr} %}${content}{% /field %}`;
|
|
590
667
|
}
|
|
591
668
|
/**
|
|
592
669
|
* Serialize a year-field.
|
|
593
670
|
*/
|
|
594
671
|
function serializeYearField(field, response) {
|
|
595
672
|
const attrs = {
|
|
673
|
+
kind: "year",
|
|
596
674
|
id: field.id,
|
|
597
675
|
label: field.label
|
|
598
676
|
};
|
|
@@ -612,7 +690,74 @@ function serializeYearField(field, response) {
|
|
|
612
690
|
}
|
|
613
691
|
const sentinelContent = getSentinelContent(response);
|
|
614
692
|
if (sentinelContent) content = sentinelContent;
|
|
615
|
-
return `{%
|
|
693
|
+
return `{% field ${attrStr} %}${content}{% /field %}`;
|
|
694
|
+
}
|
|
695
|
+
/**
|
|
696
|
+
* Serialize a cell value for table output.
|
|
697
|
+
*/
|
|
698
|
+
function serializeCellValue(cell, _columnType) {
|
|
699
|
+
if (cell.state === "skipped") return cell.reason ? `%SKIP:${cell.reason}%` : "%SKIP%";
|
|
700
|
+
if (cell.state === "aborted") return cell.reason ? `%ABORT:${cell.reason}%` : "%ABORT%";
|
|
701
|
+
if (cell.value === void 0 || cell.value === null) return "";
|
|
702
|
+
if (typeof cell.value === "number") return String(cell.value);
|
|
703
|
+
return cell.value;
|
|
704
|
+
}
|
|
705
|
+
/**
|
|
706
|
+
* Serialize a table row to markdown table row format.
|
|
707
|
+
*/
|
|
708
|
+
function serializeTableRow(row, columns) {
|
|
709
|
+
return `| ${columns.map((col) => {
|
|
710
|
+
return serializeCellValue(row[col.id] ?? { state: "skipped" }, col.type);
|
|
711
|
+
}).join(" | ")} |`;
|
|
712
|
+
}
|
|
713
|
+
/**
|
|
714
|
+
* Serialize a table value to markdown table format.
|
|
715
|
+
*/
|
|
716
|
+
function serializeMarkdownTable(value, columns) {
|
|
717
|
+
if (columns.length === 0) return "";
|
|
718
|
+
const lines = [];
|
|
719
|
+
const headerCells = columns.map((col) => col.label);
|
|
720
|
+
lines.push(`| ${headerCells.join(" | ")} |`);
|
|
721
|
+
const separatorCells = columns.map(() => "---");
|
|
722
|
+
lines.push(`| ${separatorCells.join(" | ")} |`);
|
|
723
|
+
for (const row of value.rows) lines.push(serializeTableRow(row, columns));
|
|
724
|
+
return lines.join("\n");
|
|
725
|
+
}
|
|
726
|
+
/**
|
|
727
|
+
* Serialize a table-field.
|
|
728
|
+
*/
|
|
729
|
+
function serializeTableField(field, response) {
|
|
730
|
+
const attrs = {
|
|
731
|
+
kind: "table",
|
|
732
|
+
id: field.id,
|
|
733
|
+
label: field.label
|
|
734
|
+
};
|
|
735
|
+
if (field.required) attrs.required = field.required;
|
|
736
|
+
if (field.priority !== DEFAULT_PRIORITY) attrs.priority = field.priority;
|
|
737
|
+
if (field.role !== AGENT_ROLE) attrs.role = field.role;
|
|
738
|
+
attrs.columnIds = field.columns.map((c) => c.id);
|
|
739
|
+
attrs.columnLabels = field.columns.map((c) => c.label);
|
|
740
|
+
attrs.columnTypes = field.columns.map((c) => {
|
|
741
|
+
if (c.required) return {
|
|
742
|
+
type: c.type,
|
|
743
|
+
required: true
|
|
744
|
+
};
|
|
745
|
+
return c.type;
|
|
746
|
+
});
|
|
747
|
+
if (field.minRows !== void 0) attrs.minRows = field.minRows;
|
|
748
|
+
if (field.maxRows !== void 0) attrs.maxRows = field.maxRows;
|
|
749
|
+
if (field.validate) attrs.validate = field.validate;
|
|
750
|
+
if (field.report !== void 0) attrs.report = field.report;
|
|
751
|
+
if (response?.state === "skipped" || response?.state === "aborted") attrs.state = response.state;
|
|
752
|
+
const attrStr = serializeAttrs(attrs);
|
|
753
|
+
let content = "";
|
|
754
|
+
if (response?.state === "answered" && response.value) {
|
|
755
|
+
const value = response.value;
|
|
756
|
+
if (value.rows.length > 0) content = formatValueFence(serializeMarkdownTable(value, field.columns));
|
|
757
|
+
}
|
|
758
|
+
const sentinelContent = getSentinelContent(response);
|
|
759
|
+
if (sentinelContent) content = sentinelContent;
|
|
760
|
+
return `{% field ${attrStr} %}${content}{% /field %}`;
|
|
616
761
|
}
|
|
617
762
|
/**
|
|
618
763
|
* Serialize a field to Markdoc format.
|
|
@@ -630,6 +775,7 @@ function serializeField(field, responses) {
|
|
|
630
775
|
case "url_list": return serializeUrlListField(field, response);
|
|
631
776
|
case "date": return serializeDateField(field, response);
|
|
632
777
|
case "year": return serializeYearField(field, response);
|
|
778
|
+
case "table": return serializeTableField(field, response);
|
|
633
779
|
}
|
|
634
780
|
}
|
|
635
781
|
/**
|
|
@@ -664,13 +810,19 @@ function serializeNotes(notes) {
|
|
|
664
810
|
}
|
|
665
811
|
/**
|
|
666
812
|
* Serialize a field group.
|
|
813
|
+
* Implicit groups (fields placed directly under the form) are serialized
|
|
814
|
+
* without the group wrapper tags.
|
|
667
815
|
*/
|
|
668
816
|
function serializeFieldGroup(group, responses, docs) {
|
|
669
|
-
const
|
|
670
|
-
if (group.
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
817
|
+
const lines = [];
|
|
818
|
+
if (!group.implicit) {
|
|
819
|
+
const attrs = { id: group.id };
|
|
820
|
+
if (group.title) attrs.title = group.title;
|
|
821
|
+
if (group.validate) attrs.validate = group.validate;
|
|
822
|
+
if (group.report !== void 0) attrs.report = group.report;
|
|
823
|
+
const attrStr = serializeAttrs(attrs);
|
|
824
|
+
lines.push(`{% group ${attrStr} %}`);
|
|
825
|
+
}
|
|
674
826
|
const docsByRef = /* @__PURE__ */ new Map();
|
|
675
827
|
for (const doc of docs) {
|
|
676
828
|
const list = docsByRef.get(doc.ref) ?? [];
|
|
@@ -686,8 +838,10 @@ function serializeFieldGroup(group, responses, docs) {
|
|
|
686
838
|
lines.push(serializeDocBlock(doc));
|
|
687
839
|
}
|
|
688
840
|
}
|
|
689
|
-
|
|
690
|
-
|
|
841
|
+
if (!group.implicit) {
|
|
842
|
+
lines.push("");
|
|
843
|
+
lines.push("{% /group %}");
|
|
844
|
+
}
|
|
691
845
|
return lines.join("\n");
|
|
692
846
|
}
|
|
693
847
|
/**
|
|
@@ -819,6 +973,12 @@ function serializeFieldRaw(field, responses) {
|
|
|
819
973
|
else lines.push("_(empty)_");
|
|
820
974
|
break;
|
|
821
975
|
}
|
|
976
|
+
case "table": {
|
|
977
|
+
const tableValue = value;
|
|
978
|
+
if (tableValue?.rows && tableValue.rows.length > 0) lines.push(`_(${tableValue.rows.length} rows)_`);
|
|
979
|
+
else lines.push("_(empty)_");
|
|
980
|
+
break;
|
|
981
|
+
}
|
|
822
982
|
}
|
|
823
983
|
return lines.join("\n");
|
|
824
984
|
}
|
|
@@ -953,14 +1113,17 @@ function computeStructureSummary(schema) {
|
|
|
953
1113
|
url: 0,
|
|
954
1114
|
url_list: 0,
|
|
955
1115
|
date: 0,
|
|
956
|
-
year: 0
|
|
1116
|
+
year: 0,
|
|
1117
|
+
table: 0
|
|
957
1118
|
};
|
|
958
1119
|
const groupsById = {};
|
|
959
1120
|
const fieldsById = {};
|
|
960
1121
|
const optionsById = {};
|
|
1122
|
+
const columnsById = {};
|
|
961
1123
|
let groupCount = 0;
|
|
962
1124
|
let fieldCount = 0;
|
|
963
1125
|
let optionCount = 0;
|
|
1126
|
+
let columnCount = 0;
|
|
964
1127
|
for (const group of schema.groups) {
|
|
965
1128
|
groupCount++;
|
|
966
1129
|
groupsById[group.id] = "field_group";
|
|
@@ -976,16 +1139,26 @@ function computeStructureSummary(schema) {
|
|
|
976
1139
|
parentFieldKind: field.kind
|
|
977
1140
|
};
|
|
978
1141
|
}
|
|
1142
|
+
if (field.kind === "table") for (const column of field.columns) {
|
|
1143
|
+
columnCount++;
|
|
1144
|
+
const qualifiedRef = `${field.id}.${column.id}`;
|
|
1145
|
+
columnsById[qualifiedRef] = {
|
|
1146
|
+
parentFieldId: field.id,
|
|
1147
|
+
columnType: column.type
|
|
1148
|
+
};
|
|
1149
|
+
}
|
|
979
1150
|
}
|
|
980
1151
|
}
|
|
981
1152
|
return {
|
|
982
1153
|
groupCount,
|
|
983
1154
|
fieldCount,
|
|
984
1155
|
optionCount,
|
|
1156
|
+
columnCount,
|
|
985
1157
|
fieldCountByKind,
|
|
986
1158
|
groupsById,
|
|
987
1159
|
fieldsById,
|
|
988
|
-
optionsById
|
|
1160
|
+
optionsById,
|
|
1161
|
+
columnsById
|
|
989
1162
|
};
|
|
990
1163
|
}
|
|
991
1164
|
/**
|
|
@@ -1024,6 +1197,7 @@ function isFieldSubmitted(field, value) {
|
|
|
1024
1197
|
return v.value !== null && v.value.trim() !== "";
|
|
1025
1198
|
}
|
|
1026
1199
|
case "year": return value.value !== null;
|
|
1200
|
+
case "table": return value.rows.length > 0;
|
|
1027
1201
|
}
|
|
1028
1202
|
}
|
|
1029
1203
|
/**
|
|
@@ -1629,6 +1803,116 @@ function validateYearField(field, value) {
|
|
|
1629
1803
|
return issues;
|
|
1630
1804
|
}
|
|
1631
1805
|
/**
|
|
1806
|
+
* Validate a cell value according to its column type.
|
|
1807
|
+
*/
|
|
1808
|
+
function validateCellValue(cell, column, fieldId, rowIndex) {
|
|
1809
|
+
const issues = [];
|
|
1810
|
+
const cellRef = `${fieldId}[${rowIndex}].${column.id}`;
|
|
1811
|
+
if (cell.state === "skipped" || cell.state === "aborted") {
|
|
1812
|
+
if (column.required) issues.push({
|
|
1813
|
+
severity: "error",
|
|
1814
|
+
message: `Required cell "${column.label}" in row ${rowIndex + 1} is ${cell.state}`,
|
|
1815
|
+
ref: cellRef,
|
|
1816
|
+
source: "builtin"
|
|
1817
|
+
});
|
|
1818
|
+
return issues;
|
|
1819
|
+
}
|
|
1820
|
+
if (column.required && (cell.value === void 0 || cell.value === null || cell.value === "")) {
|
|
1821
|
+
issues.push({
|
|
1822
|
+
severity: "error",
|
|
1823
|
+
message: `Required cell "${column.label}" in row ${rowIndex + 1} is empty`,
|
|
1824
|
+
ref: cellRef,
|
|
1825
|
+
source: "builtin"
|
|
1826
|
+
});
|
|
1827
|
+
return issues;
|
|
1828
|
+
}
|
|
1829
|
+
if (cell.value === void 0 || cell.value === null || cell.value === "") return issues;
|
|
1830
|
+
switch (column.type) {
|
|
1831
|
+
case "number":
|
|
1832
|
+
if (typeof cell.value !== "number") issues.push({
|
|
1833
|
+
severity: "error",
|
|
1834
|
+
message: `Cell "${column.label}" in row ${rowIndex + 1} must be a number (got "${cell.value}")`,
|
|
1835
|
+
ref: cellRef,
|
|
1836
|
+
source: "builtin"
|
|
1837
|
+
});
|
|
1838
|
+
break;
|
|
1839
|
+
case "year":
|
|
1840
|
+
if (typeof cell.value !== "number" || !Number.isInteger(cell.value)) issues.push({
|
|
1841
|
+
severity: "error",
|
|
1842
|
+
message: `Cell "${column.label}" in row ${rowIndex + 1} must be an integer year (got "${cell.value}")`,
|
|
1843
|
+
ref: cellRef,
|
|
1844
|
+
source: "builtin"
|
|
1845
|
+
});
|
|
1846
|
+
break;
|
|
1847
|
+
case "url":
|
|
1848
|
+
if (typeof cell.value === "string") try {
|
|
1849
|
+
new URL(cell.value);
|
|
1850
|
+
} catch {
|
|
1851
|
+
issues.push({
|
|
1852
|
+
severity: "error",
|
|
1853
|
+
message: `Cell "${column.label}" in row ${rowIndex + 1} must be a valid URL (got "${cell.value}")`,
|
|
1854
|
+
ref: cellRef,
|
|
1855
|
+
source: "builtin"
|
|
1856
|
+
});
|
|
1857
|
+
}
|
|
1858
|
+
break;
|
|
1859
|
+
case "date":
|
|
1860
|
+
if (typeof cell.value === "string") {
|
|
1861
|
+
if (!/^\d{4}-\d{2}-\d{2}$/.test(cell.value)) issues.push({
|
|
1862
|
+
severity: "error",
|
|
1863
|
+
message: `Cell "${column.label}" in row ${rowIndex + 1} must be a valid date in YYYY-MM-DD format (got "${cell.value}")`,
|
|
1864
|
+
ref: cellRef,
|
|
1865
|
+
source: "builtin"
|
|
1866
|
+
});
|
|
1867
|
+
}
|
|
1868
|
+
break;
|
|
1869
|
+
case "string": break;
|
|
1870
|
+
}
|
|
1871
|
+
return issues;
|
|
1872
|
+
}
|
|
1873
|
+
/**
|
|
1874
|
+
* Validate a table row.
|
|
1875
|
+
*/
|
|
1876
|
+
function validateTableRow(row, columns, fieldId, rowIndex) {
|
|
1877
|
+
const issues = [];
|
|
1878
|
+
for (const column of columns) {
|
|
1879
|
+
const cell = row[column.id] ?? { state: "skipped" };
|
|
1880
|
+
issues.push(...validateCellValue(cell, column, fieldId, rowIndex));
|
|
1881
|
+
}
|
|
1882
|
+
return issues;
|
|
1883
|
+
}
|
|
1884
|
+
/**
|
|
1885
|
+
* Validate a table field.
|
|
1886
|
+
*/
|
|
1887
|
+
function validateTableField(field, value) {
|
|
1888
|
+
const issues = [];
|
|
1889
|
+
const isEmpty = !value || value.rows.length === 0;
|
|
1890
|
+
if (field.required && isEmpty) {
|
|
1891
|
+
issues.push({
|
|
1892
|
+
severity: "error",
|
|
1893
|
+
message: `Required field "${field.label}" is empty`,
|
|
1894
|
+
ref: field.id,
|
|
1895
|
+
source: "builtin"
|
|
1896
|
+
});
|
|
1897
|
+
return issues;
|
|
1898
|
+
}
|
|
1899
|
+
if (isEmpty) return issues;
|
|
1900
|
+
if (field.minRows !== void 0 && value.rows.length < field.minRows) issues.push({
|
|
1901
|
+
severity: "error",
|
|
1902
|
+
message: `"${field.label}" must have at least ${field.minRows} row(s) (has ${value.rows.length})`,
|
|
1903
|
+
ref: field.id,
|
|
1904
|
+
source: "builtin"
|
|
1905
|
+
});
|
|
1906
|
+
if (field.maxRows !== void 0 && value.rows.length > field.maxRows) issues.push({
|
|
1907
|
+
severity: "error",
|
|
1908
|
+
message: `"${field.label}" must have at most ${field.maxRows} row(s) (has ${value.rows.length})`,
|
|
1909
|
+
ref: field.id,
|
|
1910
|
+
source: "builtin"
|
|
1911
|
+
});
|
|
1912
|
+
for (let i = 0; i < value.rows.length; i++) issues.push(...validateTableRow(value.rows[i], field.columns, field.id, i));
|
|
1913
|
+
return issues;
|
|
1914
|
+
}
|
|
1915
|
+
/**
|
|
1632
1916
|
* Validate a single field.
|
|
1633
1917
|
*/
|
|
1634
1918
|
function validateField(field, responses) {
|
|
@@ -1645,6 +1929,7 @@ function validateField(field, responses) {
|
|
|
1645
1929
|
case "url_list": return validateUrlListField(field, value);
|
|
1646
1930
|
case "date": return validateDateField(field, value);
|
|
1647
1931
|
case "year": return validateYearField(field, value);
|
|
1932
|
+
case "table": return validateTableField(field, value);
|
|
1648
1933
|
}
|
|
1649
1934
|
}
|
|
1650
1935
|
/**
|
|
@@ -2164,6 +2449,19 @@ function validatePatch(form, patch, index) {
|
|
|
2164
2449
|
message: `Cannot apply set_year to ${field.kind} field "${field.id}"`
|
|
2165
2450
|
};
|
|
2166
2451
|
break;
|
|
2452
|
+
case "set_table": {
|
|
2453
|
+
if (field.kind !== "table") return {
|
|
2454
|
+
patchIndex: index,
|
|
2455
|
+
message: `Cannot apply set_table to ${field.kind} field "${field.id}"`
|
|
2456
|
+
};
|
|
2457
|
+
const tableField = field;
|
|
2458
|
+
const validColumns = new Set(tableField.columns.map((c) => c.id));
|
|
2459
|
+
for (const row of patch.rows) for (const colId of Object.keys(row)) if (!validColumns.has(colId)) return {
|
|
2460
|
+
patchIndex: index,
|
|
2461
|
+
message: `Invalid column "${colId}" for table field "${field.id}"`
|
|
2462
|
+
};
|
|
2463
|
+
break;
|
|
2464
|
+
}
|
|
2167
2465
|
case "clear_field": break;
|
|
2168
2466
|
case "skip_field":
|
|
2169
2467
|
if (field.required) return {
|
|
@@ -2323,6 +2621,50 @@ function applySetYear(responses, patch) {
|
|
|
2323
2621
|
};
|
|
2324
2622
|
}
|
|
2325
2623
|
/**
|
|
2624
|
+
* Convert a patch row value to a cell response.
|
|
2625
|
+
*/
|
|
2626
|
+
function patchValueToCell(value) {
|
|
2627
|
+
if (value === null || value === void 0) return { state: "skipped" };
|
|
2628
|
+
if (typeof value === "string") {
|
|
2629
|
+
const trimmed = value.trim();
|
|
2630
|
+
const skipMatch = /^%SKIP(?:[:(](.*))?[)]?%$/i.exec(trimmed);
|
|
2631
|
+
if (skipMatch) return {
|
|
2632
|
+
state: "skipped",
|
|
2633
|
+
reason: skipMatch[1]
|
|
2634
|
+
};
|
|
2635
|
+
const abortMatch = /^%ABORT(?:[:(](.*))?[)]?%$/i.exec(trimmed);
|
|
2636
|
+
if (abortMatch) return {
|
|
2637
|
+
state: "aborted",
|
|
2638
|
+
reason: abortMatch[1]
|
|
2639
|
+
};
|
|
2640
|
+
return {
|
|
2641
|
+
state: "answered",
|
|
2642
|
+
value: trimmed
|
|
2643
|
+
};
|
|
2644
|
+
}
|
|
2645
|
+
return {
|
|
2646
|
+
state: "answered",
|
|
2647
|
+
value
|
|
2648
|
+
};
|
|
2649
|
+
}
|
|
2650
|
+
/**
|
|
2651
|
+
* Apply a set_table patch.
|
|
2652
|
+
*/
|
|
2653
|
+
function applySetTable(responses, patch) {
|
|
2654
|
+
const rows = patch.rows.map((patchRow) => {
|
|
2655
|
+
const row = {};
|
|
2656
|
+
for (const [colId, value] of Object.entries(patchRow)) row[colId] = patchValueToCell(value);
|
|
2657
|
+
return row;
|
|
2658
|
+
});
|
|
2659
|
+
responses[patch.fieldId] = {
|
|
2660
|
+
state: "answered",
|
|
2661
|
+
value: {
|
|
2662
|
+
kind: "table",
|
|
2663
|
+
rows
|
|
2664
|
+
}
|
|
2665
|
+
};
|
|
2666
|
+
}
|
|
2667
|
+
/**
|
|
2326
2668
|
* Apply a clear_field patch.
|
|
2327
2669
|
*/
|
|
2328
2670
|
function applyClearField(responses, patch) {
|
|
@@ -2404,6 +2746,9 @@ function applyPatch(form, responses, patch) {
|
|
|
2404
2746
|
case "set_year":
|
|
2405
2747
|
applySetYear(responses, patch);
|
|
2406
2748
|
break;
|
|
2749
|
+
case "set_table":
|
|
2750
|
+
applySetTable(responses, patch);
|
|
2751
|
+
break;
|
|
2407
2752
|
case "clear_field":
|
|
2408
2753
|
applyClearField(responses, patch);
|
|
2409
2754
|
break;
|
|
@@ -2479,4 +2824,4 @@ function applyPatches(form, patches) {
|
|
|
2479
2824
|
}
|
|
2480
2825
|
|
|
2481
2826
|
//#endregion
|
|
2482
|
-
export { parseRolesFlag as A, DEFAULT_ROLE_INSTRUCTIONS as C, deriveReportPath as D, deriveExportPath as E, hasWebSearchSupport as F, WEB_SEARCH_CONFIG as M, formatSuggestedLlms as N, detectFileType as O, getWebSearchConfig as P, DEFAULT_ROLES as S, USER_ROLE as T, DEFAULT_MAX_TURNS as _, computeAllSummaries as a, DEFAULT_RESEARCH_MAX_ISSUES_PER_TURN as b, computeStructureSummary as c, serializeRawMarkdown as d, serializeReportMarkdown as f, DEFAULT_MAX_PATCHES_PER_TURN as g, DEFAULT_MAX_ISSUES_PER_TURN as h, validate as i, SUGGESTED_LLMS as j, getFormsDir as k, isFormComplete as l, DEFAULT_FORMS_DIR as m, getFieldsForRoles as n, computeFormState as o, AGENT_ROLE as p, inspect as r, computeProgressSummary as s, applyPatches as t, serialize as u, DEFAULT_PORT as v, REPORT_EXTENSION as w, DEFAULT_RESEARCH_MAX_PATCHES_PER_TURN as x, DEFAULT_PRIORITY as y };
|
|
2827
|
+
export { parseRolesFlag as A, DEFAULT_ROLE_INSTRUCTIONS as C, deriveReportPath as D, deriveExportPath as E, hasWebSearchSupport as F, parseModelIdForDisplay as I, WEB_SEARCH_CONFIG as M, formatSuggestedLlms as N, detectFileType as O, getWebSearchConfig as P, DEFAULT_ROLES as S, USER_ROLE as T, DEFAULT_MAX_TURNS as _, computeAllSummaries as a, DEFAULT_RESEARCH_MAX_ISSUES_PER_TURN as b, computeStructureSummary as c, serializeRawMarkdown as d, serializeReportMarkdown as f, DEFAULT_MAX_PATCHES_PER_TURN as g, DEFAULT_MAX_ISSUES_PER_TURN as h, validate as i, SUGGESTED_LLMS as j, getFormsDir as k, isFormComplete as l, DEFAULT_FORMS_DIR as m, getFieldsForRoles as n, computeFormState as o, AGENT_ROLE as p, inspect as r, computeProgressSummary as s, applyPatches as t, serialize as u, DEFAULT_PORT as v, REPORT_EXTENSION as w, DEFAULT_RESEARCH_MAX_PATCHES_PER_TURN as x, DEFAULT_PRIORITY as y };
|
package/dist/bin.mjs
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import "./coreTypes-
|
|
3
|
-
import "./apply-
|
|
4
|
-
import "./src-
|
|
5
|
-
import "./session-
|
|
6
|
-
import "./shared-
|
|
7
|
-
import { t as runCli } from "./cli-
|
|
2
|
+
import "./coreTypes-pyctKRgc.mjs";
|
|
3
|
+
import "./apply-BCCiJzQr.mjs";
|
|
4
|
+
import "./src-Df0XX7UB.mjs";
|
|
5
|
+
import "./session-uF0e6m6k.mjs";
|
|
6
|
+
import "./shared-BqPnYXrn.mjs";
|
|
7
|
+
import { t as runCli } from "./cli-D469amuk.mjs";
|
|
8
8
|
import { resolve } from "node:path";
|
|
9
9
|
import { existsSync } from "node:fs";
|
|
10
10
|
import { config } from "dotenv";
|