@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
@@ -0,0 +1,94 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.pendingAskListSchema = exports.pendingAskListItemSchema = exports.turnResultSchema = exports.pendingAskSummarySchema = exports.turnRequestSchema = exports.askResponseWithToolCallIdSchema = exports.askSurfaceSchema = void 0;
4
+ const zod_1 = require("zod");
5
+ const schema_1 = require("./schema");
6
+ const simpleCard_1 = require("./simpleCard");
7
+ const TURN_SHAPES = "prompt, askResponse, or toolCallId with buttonId";
8
+ exports.askSurfaceSchema = zod_1.z
9
+ .enum(schema_1.ASK_SURFACES)
10
+ .describe('Where the user answers. "compact" is a watch or another small screen: the agent asks only questions whose buttons show every option, and keeps replies to two short sentences. Defaults to "full".');
11
+ const toolCallIdSchema = zod_1.z.string().min(1).describe("The pending ask's toolCallId.");
12
+ /** An answer to a pending ask: the answer envelope plus the ask's `toolCallId`. */
13
+ exports.askResponseWithToolCallIdSchema = zod_1.z.discriminatedUnion("action", [
14
+ schema_1.askAcceptResponseSchema.extend({ toolCallId: toolCallIdSchema }),
15
+ schema_1.askDeclineResponseSchema.extend({ toolCallId: toolCallIdSchema }),
16
+ schema_1.askCancelResponseSchema.extend({ toolCallId: toolCallIdSchema }),
17
+ ]);
18
+ /** Puts every issue on a field, because clients show request errors next to the field they name. */
19
+ const checkTurnRequest = (body, ctx) => {
20
+ const sent = ["askResponse", "buttonId", "prompt", "toolCallId"].filter((key) => body[key] !== undefined);
21
+ const hasButton = sent.includes("buttonId") || sent.includes("toolCallId");
22
+ const shapeCount = Number(sent.includes("prompt")) + Number(sent.includes("askResponse")) + Number(hasButton);
23
+ if (shapeCount === 0) {
24
+ ctx.addIssue({ code: "custom", message: `Send one of ${TURN_SHAPES}.`, path: ["prompt"] });
25
+ return;
26
+ }
27
+ if (shapeCount > 1) {
28
+ for (const key of sent) {
29
+ ctx.addIssue({ code: "custom", message: `Send only one of ${TURN_SHAPES}.`, path: [key] });
30
+ }
31
+ return;
32
+ }
33
+ if (hasButton && body.toolCallId === undefined) {
34
+ ctx.addIssue({ code: "custom", message: "Send toolCallId with buttonId.", path: ["toolCallId"] });
35
+ }
36
+ if (hasButton && body.buttonId === undefined) {
37
+ ctx.addIssue({ code: "custom", message: "Send buttonId with toolCallId.", path: ["buttonId"] });
38
+ }
39
+ };
40
+ /** The body of `POST /gpt/histories/:id/turn`. */
41
+ exports.turnRequestSchema = zod_1.z
42
+ .object({
43
+ askResponse: exports.askResponseWithToolCallIdSchema
44
+ .optional()
45
+ .describe("A full answer to the pending ask, as a client that renders the ask sends it."),
46
+ buttonId: zod_1.z
47
+ .string()
48
+ .min(1)
49
+ .optional()
50
+ .describe("The id of the simple card button the user pressed. Send it with toolCallId."),
51
+ prompt: zod_1.z
52
+ .string()
53
+ .min(1)
54
+ .optional()
55
+ .describe("A new message from the user. It cancels the pending ask, if there is one."),
56
+ surface: exports.askSurfaceSchema.optional(),
57
+ toolCallId: toolCallIdSchema.optional(),
58
+ })
59
+ .strict()
60
+ .superRefine(checkTurnRequest)
61
+ .describe(`Send exactly one of ${TURN_SHAPES}, plus an optional surface.`);
62
+ exports.pendingAskSummarySchema = zod_1.z
63
+ .object({
64
+ kind: zod_1.z.enum(schema_1.ASK_KINDS),
65
+ simple: simpleCard_1.simpleCardSchema,
66
+ toolCallId: zod_1.z.string().min(1),
67
+ })
68
+ .strict()
69
+ .describe("The ask the turn paused on. Answer it with its toolCallId and the id of one of simple.buttons.");
70
+ /** What `POST /gpt/histories/:id/turn` returns in `data` once the turn finishes. */
71
+ exports.turnResultSchema = zod_1.z
72
+ .object({
73
+ error: zod_1.z
74
+ .string()
75
+ .optional()
76
+ .describe("Set when the turn failed after it started. text holds what the agent said before the error."),
77
+ historyId: zod_1.z.string().min(1).describe("The conversation's id."),
78
+ pendingAsk: exports.pendingAskSummarySchema.optional(),
79
+ text: zod_1.z.string().describe("The agent's reply, or an empty string when it only asked."),
80
+ title: zod_1.z.string().optional().describe("The conversation's title, once it has one."),
81
+ })
82
+ .strict();
83
+ exports.pendingAskListItemSchema = zod_1.z
84
+ .object({
85
+ created: zod_1.z.iso.datetime().describe("When the agent asked, as an ISO 8601 UTC timestamp."),
86
+ historyId: zod_1.z.string().min(1).describe("The conversation the ask belongs to."),
87
+ kind: zod_1.z.enum(schema_1.ASK_KINDS),
88
+ simple: simpleCard_1.simpleCardSchema,
89
+ title: zod_1.z.string().optional().describe("The conversation's title, once it has one."),
90
+ toolCallId: zod_1.z.string().min(1),
91
+ })
92
+ .strict();
93
+ /** What `GET /gpt/histories/pendingAsks` returns in `data`: the caller's pending asks, newest first. */
94
+ exports.pendingAskListSchema = zod_1.z.array(exports.pendingAskListItemSchema);
@@ -0,0 +1,6 @@
1
+ /**
2
+ * JSON Schema (draft-07) documents for native clients, such as a watch app generating Swift
3
+ * `Codable` types: the simple card and the bodies of the two headless endpoints. Keyed by the file
4
+ * name they are committed under in `blocks/schemas/`.
5
+ */
6
+ export declare const askJsonSchemas: () => Record<string, Record<string, unknown>>;
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.askJsonSchemas = void 0;
4
+ const zod_1 = require("zod");
5
+ const headless_1 = require("./headless");
6
+ const schema_1 = require("./schema");
7
+ const simpleCard_1 = require("./simpleCard");
8
+ /** Titles for nested schemas, so types generated from the documents get readable names. */
9
+ const NESTED_TITLES = new Map([
10
+ [schema_1.askResponseSchema, "AskResponse"],
11
+ [headless_1.askResponseWithToolCallIdSchema, "AskAnswer"],
12
+ [headless_1.pendingAskListItemSchema, "PendingAsk"],
13
+ [headless_1.pendingAskSummarySchema, "PendingAskSummary"],
14
+ [simpleCard_1.simpleCardButtonSchema, "SimpleCardButton"],
15
+ [simpleCard_1.simpleCardSchema, "SimpleCard"],
16
+ [headless_1.turnResultSchema, "TurnResult"],
17
+ ]);
18
+ const requestIdSchema = zod_1.z
19
+ .string()
20
+ .optional()
21
+ .describe("The request's id. Apps built with TerrenoApp add it to every JSON response; quote it when reporting a problem.");
22
+ const DOCUMENTS = {
23
+ "pendingAsksResponse.schema.json": {
24
+ description: "The response body of GET /gpt/histories/pendingAsks: the caller's pending asks, newest first.",
25
+ schema: zod_1.z.object({ data: headless_1.pendingAskListSchema, requestId: requestIdSchema }).strict(),
26
+ title: "PendingAsksResponse",
27
+ },
28
+ "simpleCard.schema.json": {
29
+ description: "The small-screen form of an ask: short text and at most three buttons, each carrying the exact answer it sends. Button ids are unique within a card, which JSON Schema cannot express.",
30
+ schema: simpleCard_1.simpleCardSchema,
31
+ title: "SimpleCard",
32
+ },
33
+ "turnRequest.schema.json": {
34
+ description: "The request body of POST /gpt/histories/{id}/turn. Send exactly one of prompt, askResponse, or toolCallId with buttonId, plus an optional surface. JSON Schema cannot express that rule; the server enforces it.",
35
+ schema: headless_1.turnRequestSchema,
36
+ title: "TurnRequest",
37
+ },
38
+ "turnResponse.schema.json": {
39
+ description: "The response body of POST /gpt/histories/{id}/turn, sent once the turn finishes.",
40
+ schema: zod_1.z.object({ data: headless_1.turnResultSchema, requestId: requestIdSchema }).strict(),
41
+ title: "TurnResponse",
42
+ },
43
+ };
44
+ const toDocument = ({ description, schema, title }) => {
45
+ const { $schema, description: _schemaDescription, title: _schemaTitle, ...rest } = zod_1.z.toJSONSchema(schema, {
46
+ override: ({ jsonSchema, zodSchema }) => {
47
+ const nestedTitle = NESTED_TITLES.get(zodSchema);
48
+ if (nestedTitle) {
49
+ jsonSchema.title = nestedTitle;
50
+ }
51
+ },
52
+ target: "draft-7",
53
+ });
54
+ return { $schema, description, title, ...rest };
55
+ };
56
+ /**
57
+ * JSON Schema (draft-07) documents for native clients, such as a watch app generating Swift
58
+ * `Codable` types: the simple card and the bodies of the two headless endpoints. Keyed by the file
59
+ * name they are committed under in `blocks/schemas/`.
60
+ */
61
+ const askJsonSchemas = () => Object.fromEntries(Object.entries(DOCUMENTS).map(([fileName, document]) => [fileName, toDocument(document)]));
62
+ exports.askJsonSchemas = askJsonSchemas;
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Hard limits for asks. The schemas, the prompt section, the docs, and the tests all read these
3
+ * values, so change them here only.
4
+ */
5
+ export declare const ASK_LIMITS: {
6
+ readonly cancelReasonMaxLength: 200;
7
+ readonly choice: {
8
+ readonly optionDescriptionMaxLength: 280;
9
+ readonly optionIdMaxLength: 64;
10
+ /** Must allow exactly `optionIdMaxLength` characters. */
11
+ readonly optionIdPattern: RegExp;
12
+ readonly optionLabelMaxLength: 120;
13
+ readonly optionsMax: 50;
14
+ readonly optionsMin: 2;
15
+ readonly otherMaxLength: 500;
16
+ };
17
+ readonly confirm: {
18
+ /** Every confirm label fits a simple card button uncut. */
19
+ readonly labelMaxLength: 20;
20
+ };
21
+ readonly files: {
22
+ /** The per-file cap when the host sets no `maxFileSizeBytes`, matching `/files/upload`. */
23
+ readonly defaultMaxFileSizeBytes: number;
24
+ readonly filenameMaxLength: 255;
25
+ readonly maxFiles: 10;
26
+ readonly minFiles: 1;
27
+ /** Text, CSV, and JSON files reach the model cut to this many bytes, with a note. */
28
+ readonly textMaxBytes: 100000;
29
+ };
30
+ readonly form: {
31
+ readonly fieldsMax: 8;
32
+ readonly fieldsMin: 1;
33
+ readonly helperTextMaxLength: 280;
34
+ readonly labelMaxLength: 120;
35
+ /** The fewest and most digits in a `phone` value. */
36
+ readonly phoneDigitsMax: 15;
37
+ readonly phoneDigitsMin: 7;
38
+ /** The cap on a `textarea` value and on a `textarea` field's `maxLength`. */
39
+ readonly textareaMaxLength: 10000;
40
+ /** The cap on a `text`, `email`, `url`, or `phone` value, and on a `text` field's `maxLength`. */
41
+ readonly textMaxLength: 2000;
42
+ };
43
+ readonly markdown: {
44
+ /** The cap on `initial`, on `maxLength`, and on every answer. */
45
+ readonly maxLength: 20000;
46
+ readonly placeholderMaxLength: 120;
47
+ };
48
+ readonly pendingAsksPerHistory: 1;
49
+ readonly promptMaxLength: 500;
50
+ readonly simpleCard: {
51
+ readonly buttonLabelMaxLength: 20;
52
+ readonly buttonsMax: 3;
53
+ readonly textMaxLength: 140;
54
+ readonly titleMaxLength: 40;
55
+ };
56
+ readonly submitLabelMaxLength: 24;
57
+ readonly titleMaxLength: 80;
58
+ };
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ASK_LIMITS = void 0;
4
+ /**
5
+ * Hard limits for asks. The schemas, the prompt section, the docs, and the tests all read these
6
+ * values, so change them here only.
7
+ */
8
+ exports.ASK_LIMITS = {
9
+ cancelReasonMaxLength: 200,
10
+ choice: {
11
+ optionDescriptionMaxLength: 280,
12
+ optionIdMaxLength: 64,
13
+ /** Must allow exactly `optionIdMaxLength` characters. */
14
+ optionIdPattern: /^[a-z0-9][a-z0-9_-]{0,63}$/,
15
+ optionLabelMaxLength: 120,
16
+ optionsMax: 50,
17
+ optionsMin: 2,
18
+ otherMaxLength: 500,
19
+ },
20
+ confirm: {
21
+ /** Every confirm label fits a simple card button uncut. */
22
+ labelMaxLength: 20,
23
+ },
24
+ files: {
25
+ /** The per-file cap when the host sets no `maxFileSizeBytes`, matching `/files/upload`. */
26
+ defaultMaxFileSizeBytes: 10 * 1024 * 1024,
27
+ filenameMaxLength: 255,
28
+ maxFiles: 10,
29
+ minFiles: 1,
30
+ /** Text, CSV, and JSON files reach the model cut to this many bytes, with a note. */
31
+ textMaxBytes: 100_000,
32
+ },
33
+ form: {
34
+ fieldsMax: 8,
35
+ fieldsMin: 1,
36
+ helperTextMaxLength: 280,
37
+ labelMaxLength: 120,
38
+ /** The fewest and most digits in a `phone` value. */
39
+ phoneDigitsMax: 15,
40
+ phoneDigitsMin: 7,
41
+ /** The cap on a `textarea` value and on a `textarea` field's `maxLength`. */
42
+ textareaMaxLength: 10_000,
43
+ /** The cap on a `text`, `email`, `url`, or `phone` value, and on a `text` field's `maxLength`. */
44
+ textMaxLength: 2000,
45
+ },
46
+ markdown: {
47
+ /** The cap on `initial`, on `maxLength`, and on every answer. */
48
+ maxLength: 20_000,
49
+ placeholderMaxLength: 120,
50
+ },
51
+ pendingAsksPerHistory: 1,
52
+ promptMaxLength: 500,
53
+ simpleCard: {
54
+ buttonLabelMaxLength: 20,
55
+ buttonsMax: 3,
56
+ textMaxLength: 140,
57
+ titleMaxLength: 40,
58
+ },
59
+ submitLabelMaxLength: 24,
60
+ titleMaxLength: 80,
61
+ };
@@ -0,0 +1,10 @@
1
+ import { type AskKind, type AskSurface } from "./schema";
2
+ /**
3
+ * The system prompt section that describes the enabled ask kinds and their limits. Every number
4
+ * comes from `ASK_LIMITS`. With `surface: "compact"`, it lists only the kinds the compact surface
5
+ * offers, with their narrowed limits. Returns an empty string when no kinds are offered.
6
+ */
7
+ export declare const askPromptSection: ({ kinds, surface, }: {
8
+ kinds: readonly AskKind[];
9
+ surface?: AskSurface;
10
+ }) => string;
@@ -0,0 +1,144 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.askPromptSection = void 0;
4
+ const limits_1 = require("./limits");
5
+ const schema_1 = require("./schema");
6
+ const CHOICE_ANSWER_EXAMPLE = '- An accepted answer looks like {"action": "accept", "content": {"selected": ["<id>"]}}.';
7
+ const CHOICE_DECLINE_RULE = "- allowDecline: optional, default true (the user sees Skip). Set it to false only when you cannot continue without an answer.";
8
+ const CHOICE_ID_RULE = `- id: 1-${limits_1.ASK_LIMITS.choice.optionIdMaxLength} lowercase letters, digits, "_", or "-", starting with a letter or digit. Unique within the ask.`;
9
+ const sharedRules = () => [
10
+ "Rules for every ask:",
11
+ `- prompt: required. Plain text with no markdown and no links, 1-${limits_1.ASK_LIMITS.promptMaxLength} characters.`,
12
+ `- title: optional, at most ${limits_1.ASK_LIMITS.titleMaxLength} characters.`,
13
+ "- Each ask kind below lists its other fields, including whether the user can skip it.",
14
+ ].join("\n");
15
+ const compactSharedRules = () => [
16
+ sharedRules(),
17
+ `- The screen is small: it shows only the first ${limits_1.ASK_LIMITS.simpleCard.textMaxLength} characters of prompt and the first ${limits_1.ASK_LIMITS.simpleCard.titleMaxLength} characters of title, so keep both within those lengths.`,
18
+ ].join("\n");
19
+ const choiceRules = () => {
20
+ const { choice, simpleCard } = limits_1.ASK_LIMITS;
21
+ return [
22
+ "ask_choice: the user picks one or more options from a list you provide.",
23
+ '- select: "one" for exactly one option, or "many" to let the user pick several.',
24
+ `- options: ${choice.optionsMin}-${choice.optionsMax} items, each {id, label, description?}.`,
25
+ CHOICE_ID_RULE,
26
+ `- label: at most ${choice.optionLabelMaxLength} characters. description: optional, at most ${choice.optionDescriptionMaxLength} characters.`,
27
+ '- default: optional list of option ids to preselect, each listed once. With "one", at most one id; with "many", at most maxSelected ids.',
28
+ '- minSelected, maxSelected: optional whole numbers, "many" only. The user picks from minSelected (default 1, at least 0) to maxSelected (default: every choice) choices.',
29
+ `- allowOther: optional, "many" only. true adds a text field where the user types an answer of their own, up to ${choice.otherMaxLength} characters. It counts as one choice. otherLabel: optional label for that field, at most ${choice.optionLabelMaxLength} characters. For one option or Other, use "many" with maxSelected 1.`,
30
+ `- submitLabel: optional label for the submit button, at most ${limits_1.ASK_LIMITS.submitLabelMaxLength} characters.`,
31
+ CHOICE_DECLINE_RULE,
32
+ `- Prefer select "one" with at most ${simpleCard.buttonsMax} options with labels of ${simpleCard.buttonLabelMaxLength} characters or fewer; small screens show those as buttons.`,
33
+ CHOICE_ANSWER_EXAMPLE,
34
+ '- With Other, it looks like {"action": "accept", "content": {"selected": ["<id>"], "other": "<text the user typed>"}}.',
35
+ ].join("\n");
36
+ };
37
+ const compactChoiceRules = () => {
38
+ const { choice, simpleCard } = limits_1.ASK_LIMITS;
39
+ return [
40
+ "ask_choice: the user picks one option by tapping its button.",
41
+ '- select: always "one".',
42
+ `- options: ${choice.optionsMin}-${simpleCard.buttonsMax} items, each {id, label, description?}.`,
43
+ CHOICE_ID_RULE,
44
+ `- label: at most ${simpleCard.buttonLabelMaxLength} characters, counting each emoji as 2 or more, and no two options share a label.`,
45
+ `- description: optional, at most ${choice.optionDescriptionMaxLength} characters. The buttons show only labels, so put what the user needs to decide in prompt and the labels.`,
46
+ "- default: optional list with at most one option id to preselect.",
47
+ CHOICE_DECLINE_RULE,
48
+ CHOICE_ANSWER_EXAMPLE,
49
+ ].join("\n");
50
+ };
51
+ /** A confirm always fits a simple card, so the full and compact surfaces share these rules. */
52
+ const confirmRules = () => {
53
+ const { labelMaxLength } = limits_1.ASK_LIMITS.confirm;
54
+ return [
55
+ "ask_confirm: the user approves or denies one action you describe in prompt.",
56
+ `- confirmLabel, denyLabel: optional labels for the approve and deny buttons, at most ${labelMaxLength} characters each, counting each emoji as 2 or more, and different from each other. They default to "Confirm" and "Cancel". Name the action, such as "Delete 14 todos" and "Keep them".`,
57
+ "- destructive: optional, default false. Set it to true when the action deletes data or cannot be undone; the approve button then shows as destructive.",
58
+ "- allowDecline: optional, default false, because the deny button is the negative answer. Set it to true to show Skip as well.",
59
+ `- Before you call a tool that deletes data, sends something on the user's behalf, spends money, or cannot be undone, call ask_confirm and say in prompt exactly what will happen. Make the call only after {"confirmed": true}.`,
60
+ '- An accepted answer looks like {"action": "accept", "content": {"confirmed": true}}. {"confirmed": false} means the user said no, so do not take the action.',
61
+ ].join("\n");
62
+ };
63
+ const markdownRules = () => {
64
+ const { markdown } = limits_1.ASK_LIMITS;
65
+ return [
66
+ "ask_markdown: the user edits a markdown draft you write and sends it back.",
67
+ `- initial: optional, the draft in markdown, at most ${markdown.maxLength} characters. Put the whole draft here, not in prompt.`,
68
+ `- minLength, maxLength: optional whole numbers. The answer must have at least minLength (default 0) and at most maxLength (default ${markdown.maxLength}, the most allowed) characters. minLength must not be more than maxLength.`,
69
+ `- placeholder: optional hint shown while the editor is empty, at most ${markdown.placeholderMaxLength} characters.`,
70
+ `- submitLabel: optional label for the submit button, at most ${limits_1.ASK_LIMITS.submitLabelMaxLength} characters.`,
71
+ "- allowDecline: optional, default true (the user sees Skip).",
72
+ '- An accepted answer looks like {"action": "accept", "content": {"markdown": "<the text>", "changed": true}}. changed is false when the user sent your draft unchanged.',
73
+ ].join("\n");
74
+ };
75
+ const formRules = () => {
76
+ const { choice, form } = limits_1.ASK_LIMITS;
77
+ return [
78
+ "ask_form: the user fills in a few fields and submits them at once.",
79
+ `- fields: ${form.fieldsMin}-${form.fieldsMax} items, in display order, each {id, type, label, helperText?, required?, default?} plus the rules of its type. Fields are flat: no nesting, no conditional fields, and no password fields.`,
80
+ `- id: 1-${choice.optionIdMaxLength} lowercase letters, digits, "_", or "-", starting with a letter or digit. Unique within the form. It keys the field's value in the answer.`,
81
+ `- label: at most ${form.labelMaxLength} characters. helperText: optional, at most ${form.helperTextMaxLength} characters. required: optional, default false; true means the answer must hold a non-blank value.`,
82
+ `- type "text" (one line) or "textarea" (several lines): optional minLength and maxLength. maxLength is at most ${form.textMaxLength} for text and ${form.textareaMaxLength} for textarea. The value is a string.`,
83
+ `- type "email", "url" (http or https), or "phone" (${form.phoneDigitsMin}-${form.phoneDigitsMax} digits): the value is a string in that format.`,
84
+ '- type "number": optional min, max, and integer (true for whole numbers only). The value is a number.',
85
+ '- type "date" (YYYY-MM-DD), "time" (24-hour HH:mm), or "datetime" (ISO 8601 with Z or an offset; seconds are optional, such as "2026-10-01T09:30Z" or "2026-10-01T09:30:00+02:00"): the value is a string in that format.',
86
+ '- type "boolean": a checkbox. The value is true or false.',
87
+ `- type "select" (pick one) or "multiselect" (pick any): options, ${choice.optionsMin}-${choice.optionsMax} items, each {id, label}. The value is an option id, or a list of option ids.`,
88
+ "- default: optional, a value the field accepts, filled in when the form opens.",
89
+ `- submitLabel: optional label for the submit button, at most ${limits_1.ASK_LIMITS.submitLabelMaxLength} characters.`,
90
+ "- allowDecline: optional, default true (the user sees Skip).",
91
+ '- An accepted answer looks like {"action": "accept", "content": {"values": {"<field id>": <value>}}}. Optional fields the user left empty are left out.',
92
+ ].join("\n");
93
+ };
94
+ const filesRules = () => {
95
+ const { files } = limits_1.ASK_LIMITS;
96
+ return [
97
+ "ask_files: the user uploads one or more images or documents.",
98
+ '- accept: required list, each value once: "image" (JPEG, PNG, GIF, or WebP), "pdf", "text" (plain text), "csv", or "json".',
99
+ `- minFiles, maxFiles: optional whole numbers from ${files.minFiles} to ${files.maxFiles}. The user sends from minFiles (default ${files.minFiles}) to maxFiles (default ${files.maxFiles}) files.`,
100
+ `- submitLabel: optional label for the submit button, at most ${limits_1.ASK_LIMITS.submitLabelMaxLength} characters.`,
101
+ "- allowDecline: optional, default true (the user sees Skip).",
102
+ `- The answer lists each file's filename, mimeType, and size, and the tool result then shows you each file: images and PDFs as they are, and text, CSV, and JSON as text cut to ${files.textMaxBytes} bytes.`,
103
+ ].join("\n");
104
+ };
105
+ const KIND_RULES = {
106
+ choice: choiceRules,
107
+ confirm: confirmRules,
108
+ files: filesRules,
109
+ form: formRules,
110
+ markdown: markdownRules,
111
+ };
112
+ const COMPACT_KIND_RULES = {
113
+ choice: compactChoiceRules,
114
+ confirm: confirmRules,
115
+ };
116
+ const formatSection = ({ kindRules, kinds, shared, }) => {
117
+ if (kinds.length === 0) {
118
+ return "";
119
+ }
120
+ const toolNames = kinds.map((kind) => `ask_${kind}`).join(", ");
121
+ return [`Ask tools you can call: ${toolNames}.`, shared, ...kindRules].join("\n\n");
122
+ };
123
+ /**
124
+ * The system prompt section that describes the enabled ask kinds and their limits. Every number
125
+ * comes from `ASK_LIMITS`. With `surface: "compact"`, it lists only the kinds the compact surface
126
+ * offers, with their narrowed limits. Returns an empty string when no kinds are offered.
127
+ */
128
+ const askPromptSection = ({ kinds, surface = "full", }) => {
129
+ const uniqueKinds = [...new Set(kinds)];
130
+ if (surface === "compact") {
131
+ const compactKinds = uniqueKinds.filter(schema_1.isCompactAskKind);
132
+ return formatSection({
133
+ kindRules: compactKinds.map((kind) => COMPACT_KIND_RULES[kind]()),
134
+ kinds: compactKinds,
135
+ shared: compactSharedRules(),
136
+ });
137
+ }
138
+ return formatSection({
139
+ kindRules: uniqueKinds.map((kind) => KIND_RULES[kind]()),
140
+ kinds: uniqueKinds,
141
+ shared: sharedRules(),
142
+ });
143
+ };
144
+ exports.askPromptSection = askPromptSection;