@terreno/blocks 57.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (237) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +62 -0
  3. package/biome.jsonc +18 -0
  4. package/bunfig.toml +6 -0
  5. package/dist/asks/errors.d.ts +77 -0
  6. package/dist/asks/errors.js +364 -0
  7. package/dist/asks/files.d.ts +53 -0
  8. package/dist/asks/files.js +171 -0
  9. package/dist/asks/formValues.d.ts +32 -0
  10. package/dist/asks/formValues.js +229 -0
  11. package/dist/asks/headless.d.ts +227 -0
  12. package/dist/asks/headless.js +94 -0
  13. package/dist/asks/jsonSchema.d.ts +6 -0
  14. package/dist/asks/jsonSchema.js +62 -0
  15. package/dist/asks/limits.d.ts +58 -0
  16. package/dist/asks/limits.js +61 -0
  17. package/dist/asks/prompt.d.ts +10 -0
  18. package/dist/asks/prompt.js +144 -0
  19. package/dist/asks/schema.d.ts +826 -0
  20. package/dist/asks/schema.js +864 -0
  21. package/dist/asks/simpleCard.d.ts +85 -0
  22. package/dist/asks/simpleCard.js +279 -0
  23. package/dist/asks/validateInput.d.ts +13 -0
  24. package/dist/asks/validateInput.js +19 -0
  25. package/dist/asks/validateResponse.d.ts +13 -0
  26. package/dist/asks/validateResponse.js +278 -0
  27. package/dist/cli.d.ts +11 -0
  28. package/dist/cli.js +58 -0
  29. package/dist/errors.d.ts +51 -0
  30. package/dist/errors.js +106 -0
  31. package/dist/index.d.ts +31 -0
  32. package/dist/index.js +111 -0
  33. package/dist/jsonSchema.d.ts +5 -0
  34. package/dist/jsonSchema.js +12 -0
  35. package/dist/limits.d.ts +27 -0
  36. package/dist/limits.js +30 -0
  37. package/dist/lint.d.ts +21 -0
  38. package/dist/lint.js +388 -0
  39. package/dist/parse.d.ts +16 -0
  40. package/dist/parse.js +95 -0
  41. package/dist/parsePartial.d.ts +12 -0
  42. package/dist/parsePartial.js +105 -0
  43. package/dist/prompt.d.ts +9 -0
  44. package/dist/prompt.js +95 -0
  45. package/dist/schema.d.ts +199 -0
  46. package/dist/schema.js +277 -0
  47. package/dist/validate.d.ts +17 -0
  48. package/dist/validate.js +331 -0
  49. package/package.json +60 -0
  50. package/schemas/pendingAsksResponse.schema.json +195 -0
  51. package/schemas/simpleCard.schema.json +135 -0
  52. package/schemas/turnRequest.schema.json +108 -0
  53. package/schemas/turnResponse.schema.json +204 -0
  54. package/scripts/writeJsonSchemas.ts +11 -0
  55. package/src/asks/docParity.test.ts +65 -0
  56. package/src/asks/errors.test.ts +250 -0
  57. package/src/asks/errors.ts +449 -0
  58. package/src/asks/files.test.ts +294 -0
  59. package/src/asks/files.ts +216 -0
  60. package/src/asks/fixtures/invalid/choice-blank-text.json +20 -0
  61. package/src/asks/fixtures/invalid/choice-default-not-in-options.json +13 -0
  62. package/src/asks/fixtures/invalid/choice-default-selects-two.json +16 -0
  63. package/src/asks/fixtures/invalid/choice-description-too-long.json +24 -0
  64. package/src/asks/fixtures/invalid/choice-duplicate-ids.json +17 -0
  65. package/src/asks/fixtures/invalid/choice-label-too-long.json +23 -0
  66. package/src/asks/fixtures/invalid/choice-many-bounds-not-whole.json +17 -0
  67. package/src/asks/fixtures/invalid/choice-many-bounds-out-of-range.json +17 -0
  68. package/src/asks/fixtures/invalid/choice-many-default-duplicate.json +17 -0
  69. package/src/asks/fixtures/invalid/choice-many-default-too-many.json +15 -0
  70. package/src/asks/fixtures/invalid/choice-many-max-above-options.json +14 -0
  71. package/src/asks/fixtures/invalid/choice-many-min-above-choices.json +13 -0
  72. package/src/asks/fixtures/invalid/choice-many-min-above-max.json +15 -0
  73. package/src/asks/fixtures/invalid/choice-missing-required.json +14 -0
  74. package/src/asks/fixtures/invalid/choice-mixed-errors.json +19 -0
  75. package/src/asks/fixtures/invalid/choice-not-an-object.json +5 -0
  76. package/src/asks/fixtures/invalid/choice-one-with-many-fields.json +19 -0
  77. package/src/asks/fixtures/invalid/choice-option-id-format.json +35 -0
  78. package/src/asks/fixtures/invalid/choice-options-not-objects.json +12 -0
  79. package/src/asks/fixtures/invalid/choice-other-label-too-long.json +25 -0
  80. package/src/asks/fixtures/invalid/choice-other-label-without-allow-other.json +14 -0
  81. package/src/asks/fixtures/invalid/choice-prompt-empty.json +12 -0
  82. package/src/asks/fixtures/invalid/choice-prompt-too-long.json +23 -0
  83. package/src/asks/fixtures/invalid/choice-select-unknown.json +12 -0
  84. package/src/asks/fixtures/invalid/choice-submit-label-too-long.json +13 -0
  85. package/src/asks/fixtures/invalid/choice-title-too-long.json +24 -0
  86. package/src/asks/fixtures/invalid/choice-too-few-options.json +9 -0
  87. package/src/asks/fixtures/invalid/choice-too-many-options.json +219 -0
  88. package/src/asks/fixtures/invalid/choice-unknown-key-option.json +15 -0
  89. package/src/asks/fixtures/invalid/choice-unknown-key-root.json +13 -0
  90. package/src/asks/fixtures/invalid/choice-wrong-types.json +16 -0
  91. package/src/asks/fixtures/invalid/confirm-emoji-label-too-long.json +13 -0
  92. package/src/asks/fixtures/invalid/confirm-label-too-long.json +14 -0
  93. package/src/asks/fixtures/invalid/confirm-labels-alike-after-trim.json +14 -0
  94. package/src/asks/fixtures/invalid/confirm-labels-alike.json +13 -0
  95. package/src/asks/fixtures/invalid/confirm-missing-prompt-blank-label.json +16 -0
  96. package/src/asks/fixtures/invalid/confirm-unknown-keys.json +23 -0
  97. package/src/asks/fixtures/invalid/confirm-wrong-types.json +18 -0
  98. package/src/asks/fixtures/invalid/files-accept-duplicate.json +13 -0
  99. package/src/asks/fixtures/invalid/files-accept-empty.json +13 -0
  100. package/src/asks/fixtures/invalid/files-accept-unknown.json +13 -0
  101. package/src/asks/fixtures/invalid/files-counts-out-of-range.json +19 -0
  102. package/src/asks/fixtures/invalid/files-min-above-max.json +15 -0
  103. package/src/asks/fixtures/invalid/files-missing-accept-unknown-key.json +17 -0
  104. package/src/asks/fixtures/invalid/form-default-bad-format.json +40 -0
  105. package/src/asks/fixtures/invalid/form-default-invalid-date.json +40 -0
  106. package/src/asks/fixtures/invalid/form-default-not-in-options.json +58 -0
  107. package/src/asks/fixtures/invalid/form-default-out-of-range.json +33 -0
  108. package/src/asks/fixtures/invalid/form-default-text-bounds.json +42 -0
  109. package/src/asks/fixtures/invalid/form-default-wrong-type.json +30 -0
  110. package/src/asks/fixtures/invalid/form-duplicate-field-ids.json +24 -0
  111. package/src/asks/fixtures/invalid/form-field-id-format.json +19 -0
  112. package/src/asks/fixtures/invalid/form-field-text-limits.json +33 -0
  113. package/src/asks/fixtures/invalid/form-keys-of-another-type.json +39 -0
  114. package/src/asks/fixtures/invalid/form-length-above-cap.json +30 -0
  115. package/src/asks/fixtures/invalid/form-min-above-max.json +32 -0
  116. package/src/asks/fixtures/invalid/form-no-fields.json +13 -0
  117. package/src/asks/fixtures/invalid/form-select-options.json +44 -0
  118. package/src/asks/fixtures/invalid/form-too-many-fields.json +59 -0
  119. package/src/asks/fixtures/invalid/form-unknown-field-type.json +27 -0
  120. package/src/asks/fixtures/invalid/markdown-bounds-out-of-range.json +18 -0
  121. package/src/asks/fixtures/invalid/markdown-initial-above-cap.json +13 -0
  122. package/src/asks/fixtures/invalid/markdown-max-above-cap.json +13 -0
  123. package/src/asks/fixtures/invalid/markdown-min-above-cap.json +13 -0
  124. package/src/asks/fixtures/invalid/markdown-min-above-max.json +14 -0
  125. package/src/asks/fixtures/invalid/markdown-placeholder-too-long.json +13 -0
  126. package/src/asks/fixtures/invalid/markdown-wrong-types-unknown-key.json +27 -0
  127. package/src/asks/fixtures/valid/choice-emoji-label-cut.json +32 -0
  128. package/src/asks/fixtures/valid/choice-labels-collide-after-cut.json +21 -0
  129. package/src/asks/fixtures/valid/choice-long-text-cut.json +41 -0
  130. package/src/asks/fixtures/valid/choice-many-default-below-min.json +24 -0
  131. package/src/asks/fixtures/valid/choice-many-every-choice-with-other.json +24 -0
  132. package/src/asks/fixtures/valid/choice-many-no-default.json +21 -0
  133. package/src/asks/fixtures/valid/choice-many-optional-no-buttons.json +20 -0
  134. package/src/asks/fixtures/valid/choice-many-options-default-label-collides.json +23 -0
  135. package/src/asks/fixtures/valid/choice-many-options-no-buttons.json +21 -0
  136. package/src/asks/fixtures/valid/choice-many-options-no-default.json +22 -0
  137. package/src/asks/fixtures/valid/choice-many-options-with-default.json +30 -0
  138. package/src/asks/fixtures/valid/choice-many-required-with-default.json +29 -0
  139. package/src/asks/fixtures/valid/choice-many-single-pick-or-other.json +29 -0
  140. package/src/asks/fixtures/valid/choice-many-toppings-with-default.json +36 -0
  141. package/src/asks/fixtures/valid/choice-max-limits.json +245 -0
  142. package/src/asks/fixtures/valid/choice-plan-with-default.json +42 -0
  143. package/src/asks/fixtures/valid/choice-two-options-no-decline.json +32 -0
  144. package/src/asks/fixtures/valid/choice-two-options.json +32 -0
  145. package/src/asks/fixtures/valid/confirm-allow-decline.json +40 -0
  146. package/src/asks/fixtures/valid/confirm-default-labels.json +36 -0
  147. package/src/asks/fixtures/valid/confirm-destructive-delete.json +41 -0
  148. package/src/asks/fixtures/valid/confirm-emoji-labels.json +38 -0
  149. package/src/asks/fixtures/valid/confirm-long-text-cut.json +40 -0
  150. package/src/asks/fixtures/valid/files-every-type.json +28 -0
  151. package/src/asks/fixtures/valid/files-one-csv-no-decline.json +16 -0
  152. package/src/asks/fixtures/valid/files-receipt-photos.json +24 -0
  153. package/src/asks/fixtures/valid/form-boolean-newsletter.json +30 -0
  154. package/src/asks/fixtures/valid/form-date-due.json +44 -0
  155. package/src/asks/fixtures/valid/form-datetime-meeting.json +44 -0
  156. package/src/asks/fixtures/valid/form-datetime-minutes-offset.json +44 -0
  157. package/src/asks/fixtures/valid/form-eight-fields.json +71 -0
  158. package/src/asks/fixtures/valid/form-email-contact.json +44 -0
  159. package/src/asks/fixtures/valid/form-invoice-details.json +68 -0
  160. package/src/asks/fixtures/valid/form-multiselect-channels.json +64 -0
  161. package/src/asks/fixtures/valid/form-number-budget.json +45 -0
  162. package/src/asks/fixtures/valid/form-phone-callback.json +44 -0
  163. package/src/asks/fixtures/valid/form-select-shipping.json +58 -0
  164. package/src/asks/fixtures/valid/form-text-required-default.json +47 -0
  165. package/src/asks/fixtures/valid/form-textarea-notes.json +23 -0
  166. package/src/asks/fixtures/valid/form-time-reminder.json +44 -0
  167. package/src/asks/fixtures/valid/form-url-website.json +30 -0
  168. package/src/asks/fixtures/valid/markdown-announcement-draft.json +39 -0
  169. package/src/asks/fixtures/valid/markdown-empty-draft.json +34 -0
  170. package/src/asks/fixtures/valid/markdown-initial-blank-below-min.json +24 -0
  171. package/src/asks/fixtures/valid/markdown-initial-over-max.json +24 -0
  172. package/src/asks/fixtures/valid/markdown-long-text-cut.json +37 -0
  173. package/src/asks/fixtures/valid/markdown-no-decline.json +28 -0
  174. package/src/asks/fixtures/valid/markdown-no-initial-with-min.json +25 -0
  175. package/src/asks/formValues.ts +320 -0
  176. package/src/asks/headless.test.ts +146 -0
  177. package/src/asks/headless.ts +130 -0
  178. package/src/asks/jsonSchema.test.ts +171 -0
  179. package/src/asks/jsonSchema.ts +89 -0
  180. package/src/asks/limits.ts +58 -0
  181. package/src/asks/prompt.test.ts +200 -0
  182. package/src/asks/prompt.ts +173 -0
  183. package/src/asks/schema.test.ts +198 -0
  184. package/src/asks/schema.ts +1203 -0
  185. package/src/asks/simpleCard.test.ts +998 -0
  186. package/src/asks/simpleCard.ts +341 -0
  187. package/src/asks/validateInput.test.ts +875 -0
  188. package/src/asks/validateInput.ts +24 -0
  189. package/src/asks/validateResponse.test.ts +740 -0
  190. package/src/asks/validateResponse.ts +341 -0
  191. package/src/blocksDocParity.test.ts +21 -0
  192. package/src/cli.test.ts +134 -0
  193. package/src/cli.ts +65 -0
  194. package/src/display.test.ts +68 -0
  195. package/src/errors.test.ts +29 -0
  196. package/src/errors.ts +125 -0
  197. package/src/fixtures/invalid/column-not-found.yaml +14 -0
  198. package/src/fixtures/invalid/column-type.yaml +14 -0
  199. package/src/fixtures/invalid/dataset-not-found.yaml +7 -0
  200. package/src/fixtures/invalid/dataset-too-large.yaml +20 -0
  201. package/src/fixtures/invalid/depth-exceeded.yaml +10 -0
  202. package/src/fixtures/invalid/duplicate-id.yaml +6 -0
  203. package/src/fixtures/invalid/invalid-enum.yaml +5 -0
  204. package/src/fixtures/invalid/key-order.yaml +4 -0
  205. package/src/fixtures/invalid/missing-required.yaml +3 -0
  206. package/src/fixtures/invalid/not-a-document.txt +1 -0
  207. package/src/fixtures/invalid/row-arity.yaml +11 -0
  208. package/src/fixtures/invalid/select-target.yaml +12 -0
  209. package/src/fixtures/invalid/table-too-wide.yaml +9 -0
  210. package/src/fixtures/invalid/too-many-points.yaml +8 -0
  211. package/src/fixtures/invalid/unknown-key.yaml +5 -0
  212. package/src/fixtures/invalid/unsupported-version.yaml +4 -0
  213. package/src/fixtures/invalid/yaml-anchor.yaml +6 -0
  214. package/src/fixtures/partial/cut-after-divider.yaml +5 -0
  215. package/src/fixtures/valid/actions.yaml +32 -0
  216. package/src/fixtures/valid/chart.yaml +20 -0
  217. package/src/fixtures/valid/layout.yaml +26 -0
  218. package/src/fixtures/valid/prose.yaml +4 -0
  219. package/src/html.test.ts +52 -0
  220. package/src/index.test.ts +114 -0
  221. package/src/index.ts +175 -0
  222. package/src/jsonSchema.test.ts +33 -0
  223. package/src/jsonSchema.ts +11 -0
  224. package/src/limits.ts +27 -0
  225. package/src/lint.test.ts +289 -0
  226. package/src/lint.ts +484 -0
  227. package/src/parse.test.ts +281 -0
  228. package/src/parse.ts +104 -0
  229. package/src/parsePartial.test.ts +53 -0
  230. package/src/parsePartial.ts +116 -0
  231. package/src/prompt.test.ts +24 -0
  232. package/src/prompt.ts +104 -0
  233. package/src/schema.ts +509 -0
  234. package/src/tests/askFixtures.ts +40 -0
  235. package/src/validate.perf.test.ts +43 -0
  236. package/src/validate.ts +369 -0
  237. package/tsconfig.json +18 -0
package/dist/parse.js ADDED
@@ -0,0 +1,95 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseBlocks = exports.stripBlocksFence = void 0;
4
+ const yaml_1 = require("yaml");
5
+ const errors_1 = require("./errors");
6
+ const FENCE = /^```(?:yaml|yml|json)?[^\n]*\n([\s\S]*?)\n?```$/i;
7
+ const notADocument = () => ({
8
+ code: "NOT_A_DOCUMENT",
9
+ fix: "Reply with one document: v, then optional datasets, then blocks.",
10
+ message: errors_1.BLOCK_ERROR_CODES.NOT_A_DOCUMENT,
11
+ path: "",
12
+ });
13
+ const yamlFeatureDisallowed = () => ({
14
+ code: "YAML_FEATURE_DISALLOWED",
15
+ fix: "Remove YAML anchors, aliases, and custom tags. Use plain mappings and sequences.",
16
+ message: errors_1.BLOCK_ERROR_CODES.YAML_FEATURE_DISALLOWED,
17
+ path: "",
18
+ });
19
+ /** Drops one surrounding markdown fence so a model that adds one still parses. */
20
+ const stripBlocksFence = (text) => {
21
+ const trimmed = text.trim();
22
+ const match = FENCE.exec(trimmed);
23
+ if (match?.[1] !== undefined) {
24
+ return match[1];
25
+ }
26
+ return trimmed;
27
+ };
28
+ exports.stripBlocksFence = stripBlocksFence;
29
+ const hasExplicitTag = (tag) => typeof tag === "string" && tag.length > 0;
30
+ const usesDisallowedYaml = (doc) => {
31
+ const hasTagError = doc.errors.some((error) => error.code === "TAG_RESOLVE_FAILED" || error.code === "BAD_ALIAS");
32
+ if (hasTagError) {
33
+ return true;
34
+ }
35
+ let disallowed = false;
36
+ (0, yaml_1.visit)(doc, {
37
+ Alias() {
38
+ disallowed = true;
39
+ },
40
+ Map(_key, node) {
41
+ if (node.anchor || hasExplicitTag(node.tag)) {
42
+ disallowed = true;
43
+ }
44
+ },
45
+ Scalar(_key, node) {
46
+ if (node.anchor || hasExplicitTag(node.tag)) {
47
+ disallowed = true;
48
+ }
49
+ },
50
+ Seq(_key, node) {
51
+ if (node.anchor || hasExplicitTag(node.tag)) {
52
+ disallowed = true;
53
+ }
54
+ },
55
+ });
56
+ return disallowed;
57
+ };
58
+ const isPlainMapping = (value) => {
59
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
60
+ return false;
61
+ }
62
+ return Object.getPrototypeOf(value) === Object.prototype;
63
+ };
64
+ /**
65
+ * Parses one assistant reply into a plain value.
66
+ * A surrounding markdown fence is ignored. Anchors, aliases, and custom tags are rejected.
67
+ * Anything that is not a mapping with `v` is `NOT_A_DOCUMENT`.
68
+ */
69
+ const parseBlocks = (text) => {
70
+ const stripped = (0, exports.stripBlocksFence)(text);
71
+ if (stripped.trim() === "") {
72
+ return { errors: [notADocument()], ok: false };
73
+ }
74
+ const docs = (0, yaml_1.parseAllDocuments)(stripped, {
75
+ schema: "core",
76
+ strict: true,
77
+ uniqueKeys: true,
78
+ });
79
+ const doc = docs[0];
80
+ if (docs.length !== 1 || doc === undefined) {
81
+ return { errors: [notADocument()], ok: false };
82
+ }
83
+ if (usesDisallowedYaml(doc)) {
84
+ return { errors: [yamlFeatureDisallowed()], ok: false };
85
+ }
86
+ if (doc.errors.length > 0) {
87
+ return { errors: [notADocument()], ok: false };
88
+ }
89
+ const value = doc.toJS();
90
+ if (!isPlainMapping(value) || !Object.hasOwn(value, "v")) {
91
+ return { errors: [notADocument()], ok: false };
92
+ }
93
+ return { ok: true, value };
94
+ };
95
+ exports.parseBlocks = parseBlocks;
@@ -0,0 +1,12 @@
1
+ export interface ParseBlocksPartialResult {
2
+ blocks: unknown[];
3
+ datasets?: Record<string, unknown>;
4
+ pending: boolean;
5
+ }
6
+ /**
7
+ * Reads a truncated reply while it is still streaming.
8
+ * A finished, valid document returns every block with `pending: false`.
9
+ * A cut reply returns only the top-level blocks that already ended (the next `- ` arrived)
10
+ * and sets `pending: true`. It never throws.
11
+ */
12
+ export declare const parseBlocksPartial: (text: string) => ParseBlocksPartialResult;
@@ -0,0 +1,105 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseBlocksPartial = void 0;
4
+ const parse_1 = require("./parse");
5
+ const validate_1 = require("./validate");
6
+ const emptyPending = () => ({ blocks: [], pending: true });
7
+ const isPlainMapping = (value) => {
8
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
9
+ return false;
10
+ }
11
+ return Object.getPrototypeOf(value) === Object.prototype;
12
+ };
13
+ const datasetsFrom = (value) => {
14
+ if (!isPlainMapping(value) || !isPlainMapping(value.datasets)) {
15
+ return undefined;
16
+ }
17
+ return value.datasets;
18
+ };
19
+ const blocksFrom = (value) => {
20
+ if (!isPlainMapping(value) || !Array.isArray(value.blocks)) {
21
+ return undefined;
22
+ }
23
+ return value.blocks;
24
+ };
25
+ /**
26
+ * Top-level block items are the lines indented exactly two spaces that start a sequence entry.
27
+ * Nested lists sit deeper, so they stay inside the current item.
28
+ */
29
+ const topLevelItems = (body) => {
30
+ const items = [];
31
+ let current = null;
32
+ for (const line of body.split("\n")) {
33
+ if (line.startsWith(" - ")) {
34
+ if (current !== null) {
35
+ items.push(current.join("\n"));
36
+ }
37
+ current = [line];
38
+ continue;
39
+ }
40
+ if (current !== null) {
41
+ current.push(line);
42
+ }
43
+ }
44
+ if (current !== null) {
45
+ items.push(current.join("\n"));
46
+ }
47
+ return items;
48
+ };
49
+ const blockFromItem = (item) => {
50
+ const parsed = (0, parse_1.parseBlocks)(`v: 1\nblocks:\n${item}\n`);
51
+ if (!parsed.ok) {
52
+ return undefined;
53
+ }
54
+ const blocks = blocksFrom(parsed.value);
55
+ return blocks?.[0];
56
+ };
57
+ /**
58
+ * Reads a truncated reply while it is still streaming.
59
+ * A finished, valid document returns every block with `pending: false`.
60
+ * A cut reply returns only the top-level blocks that already ended (the next `- ` arrived)
61
+ * and sets `pending: true`. It never throws.
62
+ */
63
+ const parseBlocksPartial = (text) => {
64
+ try {
65
+ const stripped = (0, parse_1.stripBlocksFence)(text);
66
+ if (stripped.trim() === "") {
67
+ return emptyPending();
68
+ }
69
+ const parsed = (0, parse_1.parseBlocks)(stripped);
70
+ if (parsed.ok) {
71
+ const blocks = blocksFrom(parsed.value);
72
+ if (blocks !== undefined && (0, validate_1.validateBlocks)(parsed.value).ok) {
73
+ return {
74
+ blocks,
75
+ datasets: datasetsFrom(parsed.value),
76
+ pending: false,
77
+ };
78
+ }
79
+ }
80
+ const lines = stripped.split("\n");
81
+ const blocksLine = lines.findIndex((line) => line === "blocks:" || line.startsWith("blocks: "));
82
+ if (blocksLine < 0) {
83
+ return emptyPending();
84
+ }
85
+ const prefix = lines.slice(0, blocksLine).join("\n");
86
+ let datasets;
87
+ if (prefix.includes("datasets:")) {
88
+ const closed = (0, parse_1.parseBlocks)(`${prefix}\nblocks:\n - type: divider\n`);
89
+ if (closed.ok) {
90
+ datasets = datasetsFrom(closed.value);
91
+ }
92
+ }
93
+ const items = topLevelItems(lines.slice(blocksLine + 1).join("\n"));
94
+ const completeItems = items.slice(0, -1);
95
+ const blocks = completeItems.flatMap((item) => {
96
+ const block = blockFromItem(item);
97
+ return block === undefined ? [] : [block];
98
+ });
99
+ return { blocks, datasets, pending: true };
100
+ }
101
+ catch {
102
+ return emptyPending();
103
+ }
104
+ };
105
+ exports.parseBlocksPartial = parseBlocksPartial;
@@ -0,0 +1,9 @@
1
+ /**
2
+ * The system-prompt section a host prepends when a reply must be one block document.
3
+ * Every cap is read from `BLOCK_LIMITS` so the prompt, the schema, and the reference stay aligned.
4
+ */
5
+ export declare const blocksPromptSection: ({ allowHtml, hostActions, imageHosts, }?: {
6
+ allowHtml?: boolean;
7
+ hostActions?: readonly string[];
8
+ imageHosts?: readonly string[];
9
+ }) => string;
package/dist/prompt.js ADDED
@@ -0,0 +1,95 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.blocksPromptSection = void 0;
4
+ const limits_1 = require("./limits");
5
+ const DATA_EXAMPLE = `v: 1
6
+ datasets:
7
+ signups:
8
+ columns:
9
+ - name: month
10
+ type: string
11
+ - name: count
12
+ type: number
13
+ rows:
14
+ - [Jan, 120]
15
+ - [Feb, 180]
16
+ blocks:
17
+ - type: chart
18
+ id: signups_chart
19
+ kind: bar
20
+ data: signups
21
+ x: month
22
+ y: count
23
+ title: Signups by month
24
+ - type: actions
25
+ id: followups
26
+ elements:
27
+ - type: button
28
+ id: weekly
29
+ text: Show weekly
30
+ action:
31
+ kind: reply
32
+ text: Show weekly signups for this quarter`;
33
+ const PROSE_EXAMPLE = `v: 1
34
+ blocks:
35
+ - type: text
36
+ markdown: I could not find any signups for that range. Try a wider date window.`;
37
+ /**
38
+ * The system-prompt section a host prepends when a reply must be one block document.
39
+ * Every cap is read from `BLOCK_LIMITS` so the prompt, the schema, and the reference stay aligned.
40
+ */
41
+ const blocksPromptSection = ({ allowHtml = false, hostActions = [], imageHosts = [], } = {}) => {
42
+ const callbacks = hostActions.length === 0
43
+ ? "No callback names are registered. Do not emit kind callback."
44
+ : `A callback name must be one of: ${hostActions.join(", ")}.`;
45
+ const imageHostsLine = imageHosts.length === 0
46
+ ? "Do not emit an https image src. Use a data:image URL or a file: ref."
47
+ : `An https image src must use one of these hosts: ${imageHosts.join(", ")}.`;
48
+ return [
49
+ "Your entire reply is one document; no prose outside it; no text before tool calls.",
50
+ "The document is YAML or JSON. Keys, when present, are in this order: v, datasets, blocks.",
51
+ "v is 1.",
52
+ allowHtml
53
+ ? "Block types: heading, text, metric, badge, divider, context, chart, table, actions, columns, card, html, callout, image, details."
54
+ : "Block types: heading, text, metric, badge, divider, context, chart, table, actions, columns, card, callout, image, details. Do not emit type html.",
55
+ `An html field is at most ${limits_1.BLOCK_LIMITS.htmlMaxBytes} bytes.`,
56
+ ...(allowHtml
57
+ ? [
58
+ "html requires html and may set title and height (sm, md, or lg). It is a display preview: no scripts, no links, no network.",
59
+ ]
60
+ : []),
61
+ "callout requires text and may set status to info, warning, or alert.",
62
+ "image requires alt and src. src is a data:image URL, a file: ref, or an https URL on an allowed host.",
63
+ imageHostsLine,
64
+ "details requires title and text.",
65
+ "heading requires text. text requires markdown. metric requires label and value. badge requires text. context requires text.",
66
+ "chart kind is line, bar, area, or donut. Bind it with data, x, and y, or with points of label and value.",
67
+ "table requires data, the name of a dataset. actions requires id and elements.",
68
+ "An element is a button (id, text, action) or a segmented control (id, target, options of label and data).",
69
+ "action.kind is reply (text), open (url or route, exactly one), select (target and data), or callback (name and optional payload).",
70
+ "columns has 2 to 4 children. card has children and an optional title. Do not nest columns or card inside columns or card.",
71
+ `At most ${limits_1.BLOCK_LIMITS.maxBlocks} blocks, including nested blocks.`,
72
+ `Depth is at most ${limits_1.BLOCK_LIMITS.maxDepth}.`,
73
+ `An actions block has at most ${limits_1.BLOCK_LIMITS.actionElementsMax} elements.`,
74
+ `columns has ${limits_1.BLOCK_LIMITS.columnsMin} to ${limits_1.BLOCK_LIMITS.columnsMax} children.`,
75
+ `At most ${limits_1.BLOCK_LIMITS.maxDatasets} datasets.`,
76
+ `An inline dataset has at most ${limits_1.BLOCK_LIMITS.datasetRowMax} rows and ${limits_1.BLOCK_LIMITS.datasetColumnMax} columns.`,
77
+ `A ref dataset limit is at most ${limits_1.BLOCK_LIMITS.refLimitMax}.`,
78
+ `A text block has at most ${limits_1.BLOCK_LIMITS.blockTextMaxLength} characters.`,
79
+ `A document has at most ${limits_1.BLOCK_LIMITS.documentTextMaxLength} characters of text.`,
80
+ `Heading text is at most ${limits_1.BLOCK_LIMITS.headingTextMaxLength} characters.`,
81
+ `Badge text is at most ${limits_1.BLOCK_LIMITS.badgeTextMaxLength} characters.`,
82
+ `Context text is at most ${limits_1.BLOCK_LIMITS.contextTextMaxLength} characters.`,
83
+ `A card title is at most ${limits_1.BLOCK_LIMITS.cardTitleMaxLength} characters.`,
84
+ `A metric label is at most ${limits_1.BLOCK_LIMITS.metricLabelMaxLength} characters, its value ${limits_1.BLOCK_LIMITS.metricValueMaxLength}, its delta ${limits_1.BLOCK_LIMITS.metricDeltaMaxLength}, and its helper ${limits_1.BLOCK_LIMITS.metricHelperMaxLength}.`,
85
+ `A bar chart warns above ${limits_1.BLOCK_LIMITS.barCategoryWarning} categories.`,
86
+ `A donut chart warns above ${limits_1.BLOCK_LIMITS.donutSliceWarning} slices.`,
87
+ "Use the datasetId a tool returned; never paste more than 500 rows.",
88
+ callbacks,
89
+ "Example with data:",
90
+ DATA_EXAMPLE,
91
+ "Example with prose only:",
92
+ PROSE_EXAMPLE,
93
+ ].join("\n");
94
+ };
95
+ exports.blocksPromptSection = blocksPromptSection;
@@ -0,0 +1,199 @@
1
+ import { z } from "zod";
2
+ export declare const HEADING_SIZES: readonly ["sm", "md", "lg", "xl", "2xl"];
3
+ export declare const HTML_HEIGHTS: readonly ["sm", "md", "lg"];
4
+ export declare const CALLOUT_STATUSES: readonly ["info", "warning", "alert"];
5
+ export declare const BADGE_STATUSES: readonly ["info", "error", "warning", "success", "neutral", "active"];
6
+ export declare const METRIC_TRENDS: readonly ["up", "down", "flat"];
7
+ export declare const LAYOUT_BLOCK_TYPES: readonly ["columns", "card"];
8
+ export interface HeadingBlock {
9
+ id?: string;
10
+ size?: (typeof HEADING_SIZES)[number];
11
+ text: string;
12
+ type: "heading";
13
+ }
14
+ export interface TextBlock {
15
+ id?: string;
16
+ markdown: string;
17
+ type: "text";
18
+ }
19
+ export interface MetricBlock {
20
+ delta?: string;
21
+ helper?: string;
22
+ id?: string;
23
+ label: string;
24
+ trend?: (typeof METRIC_TRENDS)[number];
25
+ type: "metric";
26
+ value: string;
27
+ }
28
+ export interface BadgeBlock {
29
+ id?: string;
30
+ status?: (typeof BADGE_STATUSES)[number];
31
+ text: string;
32
+ type: "badge";
33
+ }
34
+ export interface DividerBlock {
35
+ id?: string;
36
+ type: "divider";
37
+ }
38
+ export interface ContextBlock {
39
+ id?: string;
40
+ text: string;
41
+ type: "context";
42
+ }
43
+ export declare const CHART_KINDS: readonly ["line", "bar", "area", "donut"];
44
+ export declare const CHART_HEIGHTS: readonly ["sm", "md", "lg"];
45
+ export declare const DATASET_COLUMN_TYPES: readonly ["string", "number", "date"];
46
+ export declare const DATASET_GRAINS: readonly ["hour", "day", "week", "month"];
47
+ export interface DatasetColumn {
48
+ name: string;
49
+ type: (typeof DATASET_COLUMN_TYPES)[number];
50
+ }
51
+ export interface InlineDataset {
52
+ columns: DatasetColumn[];
53
+ rows: unknown[][];
54
+ source?: "inline";
55
+ }
56
+ export interface RefDataset {
57
+ grain?: (typeof DATASET_GRAINS)[number];
58
+ id: string;
59
+ limit?: number;
60
+ source: "ref";
61
+ }
62
+ export type Dataset = InlineDataset | RefDataset;
63
+ export interface ChartBlock {
64
+ data?: string;
65
+ emptyText?: string;
66
+ height?: (typeof CHART_HEIGHTS)[number];
67
+ id?: string;
68
+ kind: (typeof CHART_KINDS)[number];
69
+ legend?: boolean;
70
+ points?: {
71
+ label: string;
72
+ value: number;
73
+ }[];
74
+ title?: string;
75
+ type: "chart";
76
+ x?: string;
77
+ y?: string;
78
+ }
79
+ export interface TableBlock {
80
+ columns?: string[];
81
+ data: string;
82
+ id?: string;
83
+ title?: string;
84
+ type: "table";
85
+ }
86
+ export declare const BUTTON_VARIANTS: readonly ["primary", "secondary", "outline", "ghost", "destructive"];
87
+ export interface ReplyAction {
88
+ kind: "reply";
89
+ text: string;
90
+ }
91
+ export interface OpenAction {
92
+ kind: "open";
93
+ route?: string;
94
+ url?: string;
95
+ }
96
+ export interface SelectAction {
97
+ data: string;
98
+ kind: "select";
99
+ target: string;
100
+ }
101
+ export interface CallbackAction {
102
+ kind: "callback";
103
+ name: string;
104
+ payload?: Record<string, unknown>;
105
+ }
106
+ export type BlockAction = ReplyAction | OpenAction | SelectAction | CallbackAction;
107
+ export interface ButtonElement {
108
+ action: BlockAction;
109
+ iconName?: string;
110
+ id: string;
111
+ text: string;
112
+ type: "button";
113
+ variant?: (typeof BUTTON_VARIANTS)[number];
114
+ }
115
+ export interface SegmentedElement {
116
+ id: string;
117
+ options: {
118
+ data: string;
119
+ label: string;
120
+ }[];
121
+ target: string;
122
+ type: "segmented";
123
+ }
124
+ export interface ActionsBlock {
125
+ elements: (ButtonElement | SegmentedElement)[];
126
+ id: string;
127
+ type: "actions";
128
+ }
129
+ export interface HtmlBlock {
130
+ height?: (typeof HTML_HEIGHTS)[number];
131
+ html: string;
132
+ id?: string;
133
+ title?: string;
134
+ type: "html";
135
+ }
136
+ export interface CalloutBlock {
137
+ id?: string;
138
+ status?: (typeof CALLOUT_STATUSES)[number];
139
+ text: string;
140
+ type: "callout";
141
+ }
142
+ export interface ImageBlock {
143
+ alt: string;
144
+ id?: string;
145
+ src: string;
146
+ type: "image";
147
+ }
148
+ export interface DetailsBlock {
149
+ id?: string;
150
+ text: string;
151
+ title: string;
152
+ type: "details";
153
+ }
154
+ export interface ColumnsBlock {
155
+ children: Block[];
156
+ id?: string;
157
+ type: "columns";
158
+ }
159
+ export interface CardBlock {
160
+ children: Block[];
161
+ id?: string;
162
+ title?: string;
163
+ type: "card";
164
+ }
165
+ export type LeafBlock = HeadingBlock | TextBlock | MetricBlock | BadgeBlock | DividerBlock | ContextBlock | ChartBlock | TableBlock | ActionsBlock | HtmlBlock | CalloutBlock | ImageBlock | DetailsBlock;
166
+ export type Block = LeafBlock | ColumnsBlock | CardBlock;
167
+ export interface BlocksDocument {
168
+ blocks: Block[];
169
+ datasets?: Record<string, Dataset>;
170
+ v: 1;
171
+ }
172
+ /** Document schema. Semantic dataset and chart checks run in `lintDocument`. */
173
+ export declare const blocksSchema: z.ZodObject<{
174
+ blocks: z.ZodArray<z.ZodType<Block, unknown, z.core.$ZodTypeInternals<Block, unknown>>>;
175
+ datasets: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodObject<{
176
+ grain: z.ZodOptional<z.ZodEnum<{
177
+ hour: "hour";
178
+ day: "day";
179
+ week: "week";
180
+ month: "month";
181
+ }>>;
182
+ id: z.ZodString;
183
+ limit: z.ZodOptional<z.ZodNumber>;
184
+ source: z.ZodLiteral<"ref">;
185
+ }, z.core.$strict>, z.ZodObject<{
186
+ columns: z.ZodArray<z.ZodObject<{
187
+ name: z.ZodString;
188
+ type: z.ZodEnum<{
189
+ string: "string";
190
+ number: "number";
191
+ date: "date";
192
+ }>;
193
+ }, z.core.$strict>>;
194
+ rows: z.ZodArray<z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodNull]>>>;
195
+ source: z.ZodOptional<z.ZodLiteral<"inline">>;
196
+ }, z.core.$strict>]>>>;
197
+ v: z.ZodLiteral<1>;
198
+ }, z.core.$strict>;
199
+ export declare const wrapAsTextDocument: (text: string) => BlocksDocument;