@ulearning/ai-generative-frame 0.0.0-stage → 1.0.0-next.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (323) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +233 -2
  3. package/dist/assistant-ui/entry.d.ts +34 -0
  4. package/dist/assistant-ui/entry.d.ts.map +1 -0
  5. package/dist/assistant-ui/entry.js +50 -0
  6. package/dist/assistant-ui/entry.js.map +1 -0
  7. package/dist/assistant-ui/extension.d.ts +18 -0
  8. package/dist/assistant-ui/extension.d.ts.map +1 -0
  9. package/dist/assistant-ui/extension.js +0 -0
  10. package/dist/assistant-ui/history.d.ts +15 -0
  11. package/dist/assistant-ui/history.d.ts.map +1 -0
  12. package/dist/assistant-ui/history.js +39 -0
  13. package/dist/assistant-ui/history.js.map +1 -0
  14. package/dist/assistant-ui/modelContext.d.ts +45 -0
  15. package/dist/assistant-ui/modelContext.d.ts.map +1 -0
  16. package/dist/assistant-ui/modelContext.js +88 -0
  17. package/dist/assistant-ui/modelContext.js.map +1 -0
  18. package/dist/assistant-ui/snapshotSaver.d.ts +11 -0
  19. package/dist/assistant-ui/snapshotSaver.d.ts.map +1 -0
  20. package/dist/assistant-ui/snapshotSaver.js +83 -0
  21. package/dist/assistant-ui/snapshotSaver.js.map +1 -0
  22. package/dist/assistant-ui/tool-calls.d.ts +21 -0
  23. package/dist/assistant-ui/tool-calls.d.ts.map +1 -0
  24. package/dist/assistant-ui/tool-calls.js +65 -0
  25. package/dist/assistant-ui/tool-calls.js.map +1 -0
  26. package/dist/assistant-ui/toolkit.d.ts +96 -0
  27. package/dist/assistant-ui/toolkit.d.ts.map +1 -0
  28. package/dist/assistant-ui/toolkit.js +456 -0
  29. package/dist/assistant-ui/toolkit.js.map +1 -0
  30. package/dist/assistant-ui/useSharedContext.d.ts +14 -0
  31. package/dist/assistant-ui/useSharedContext.d.ts.map +1 -0
  32. package/dist/assistant-ui/useSharedContext.js +27 -0
  33. package/dist/assistant-ui/useSharedContext.js.map +1 -0
  34. package/dist/assistant-ui.d.ts +3 -0
  35. package/dist/assistant-ui.d.ts.map +1 -0
  36. package/dist/assistant-ui.js +1 -0
  37. package/dist/bootstrap.d.ts +23 -0
  38. package/dist/bootstrap.d.ts.map +1 -0
  39. package/dist/bootstrap.js +78 -0
  40. package/dist/bootstrap.js.map +1 -0
  41. package/dist/csp.d.ts +25 -0
  42. package/dist/csp.d.ts.map +1 -0
  43. package/dist/csp.js +78 -0
  44. package/dist/csp.js.map +1 -0
  45. package/dist/index.d.ts +6 -0
  46. package/dist/index.d.ts.map +1 -0
  47. package/dist/index.js +5 -0
  48. package/dist/json-schema.d.ts +70 -0
  49. package/dist/json-schema.d.ts.map +1 -0
  50. package/dist/json-schema.js +163 -0
  51. package/dist/json-schema.js.map +1 -0
  52. package/dist/preview.d.ts +30 -0
  53. package/dist/preview.d.ts.map +1 -0
  54. package/dist/preview.js +60 -0
  55. package/dist/preview.js.map +1 -0
  56. package/dist/prompts/guidance.d.ts +41 -0
  57. package/dist/prompts/guidance.d.ts.map +1 -0
  58. package/dist/prompts/guidance.js +268 -0
  59. package/dist/prompts/guidance.js.map +1 -0
  60. package/dist/prompts.d.ts +2 -0
  61. package/dist/prompts.d.ts.map +1 -0
  62. package/dist/prompts.js +1 -0
  63. package/dist/protocol.d.ts +114 -0
  64. package/dist/protocol.d.ts.map +1 -0
  65. package/dist/protocol.js +49 -0
  66. package/dist/protocol.js.map +1 -0
  67. package/dist/repair/repair.d.ts +72 -0
  68. package/dist/repair/repair.d.ts.map +1 -0
  69. package/dist/repair/repair.js +103 -0
  70. package/dist/repair/repair.js.map +1 -0
  71. package/dist/repair.d.ts +2 -0
  72. package/dist/repair.d.ts.map +1 -0
  73. package/dist/repair.js +1 -0
  74. package/dist/rpc.d.ts +60 -0
  75. package/dist/rpc.d.ts.map +1 -0
  76. package/dist/rpc.js +127 -0
  77. package/dist/rpc.js.map +1 -0
  78. package/dist/runtime/diagnostics.d.ts +17 -0
  79. package/dist/runtime/diagnostics.d.ts.map +1 -0
  80. package/dist/runtime/diagnostics.js +128 -0
  81. package/dist/runtime/diagnostics.js.map +1 -0
  82. package/dist/runtime/generated.d.ts +2 -0
  83. package/dist/runtime/generated.d.ts.map +1 -0
  84. package/dist/runtime/generated.js +5 -0
  85. package/dist/runtime/generated.js.map +1 -0
  86. package/dist/runtime/index.d.ts +7 -0
  87. package/dist/runtime/index.d.ts.map +1 -0
  88. package/dist/runtime/index.js +264 -0
  89. package/dist/runtime/index.js.map +1 -0
  90. package/dist/runtime/morph.d.ts +51 -0
  91. package/dist/runtime/morph.d.ts.map +1 -0
  92. package/dist/runtime/morph.js +251 -0
  93. package/dist/runtime/morph.js.map +1 -0
  94. package/dist/runtime/screenshot.d.ts +23 -0
  95. package/dist/runtime/screenshot.d.ts.map +1 -0
  96. package/dist/runtime/screenshot.js +188 -0
  97. package/dist/runtime/screenshot.js.map +1 -0
  98. package/dist/runtime/size.d.ts +10 -0
  99. package/dist/runtime/size.d.ts.map +1 -0
  100. package/dist/runtime/size.js +21 -0
  101. package/dist/runtime/size.js.map +1 -0
  102. package/dist/runtime/storage.d.ts +4 -0
  103. package/dist/runtime/storage.d.ts.map +1 -0
  104. package/dist/runtime/storage.js +38 -0
  105. package/dist/runtime/storage.js.map +1 -0
  106. package/dist/spec/actions.d.ts +40 -0
  107. package/dist/spec/actions.d.ts.map +1 -0
  108. package/dist/spec/actions.js +53 -0
  109. package/dist/spec/actions.js.map +1 -0
  110. package/dist/spec/assistant-ui/thread.d.ts +66 -0
  111. package/dist/spec/assistant-ui/thread.d.ts.map +1 -0
  112. package/dist/spec/assistant-ui/thread.js +252 -0
  113. package/dist/spec/assistant-ui/thread.js.map +1 -0
  114. package/dist/spec/assistant-ui/toolkit.d.ts +33 -0
  115. package/dist/spec/assistant-ui/toolkit.d.ts.map +1 -0
  116. package/dist/spec/assistant-ui/toolkit.js +142 -0
  117. package/dist/spec/assistant-ui/toolkit.js.map +1 -0
  118. package/dist/spec/assistant-ui/web.d.ts +6 -0
  119. package/dist/spec/assistant-ui/web.d.ts.map +1 -0
  120. package/dist/spec/assistant-ui/web.js +48 -0
  121. package/dist/spec/assistant-ui/web.js.map +1 -0
  122. package/dist/spec/assistant-ui.d.ts +3 -0
  123. package/dist/spec/assistant-ui.d.ts.map +1 -0
  124. package/dist/spec/assistant-ui.js +1 -0
  125. package/dist/spec/calc.d.ts +8 -0
  126. package/dist/spec/calc.d.ts.map +1 -0
  127. package/dist/spec/calc.js +498 -0
  128. package/dist/spec/calc.js.map +1 -0
  129. package/dist/spec/catalog.d.ts +52 -0
  130. package/dist/spec/catalog.d.ts.map +1 -0
  131. package/dist/spec/catalog.js +104 -0
  132. package/dist/spec/catalog.js.map +1 -0
  133. package/dist/spec/expressions.d.ts +45 -0
  134. package/dist/spec/expressions.d.ts.map +1 -0
  135. package/dist/spec/expressions.js +199 -0
  136. package/dist/spec/expressions.js.map +1 -0
  137. package/dist/spec/patch.d.ts +20 -0
  138. package/dist/spec/patch.d.ts.map +1 -0
  139. package/dist/spec/patch.js +77 -0
  140. package/dist/spec/patch.js.map +1 -0
  141. package/dist/spec/pending.d.ts +15 -0
  142. package/dist/spec/pending.d.ts.map +1 -0
  143. package/dist/spec/pending.js +33 -0
  144. package/dist/spec/pending.js.map +1 -0
  145. package/dist/spec/pointer.d.ts +17 -0
  146. package/dist/spec/pointer.d.ts.map +1 -0
  147. package/dist/spec/pointer.js +88 -0
  148. package/dist/spec/pointer.js.map +1 -0
  149. package/dist/spec/prompt.d.ts +29 -0
  150. package/dist/spec/prompt.d.ts.map +1 -0
  151. package/dist/spec/prompt.js +145 -0
  152. package/dist/spec/prompt.js.map +1 -0
  153. package/dist/spec/render-spec.d.ts +50 -0
  154. package/dist/spec/render-spec.d.ts.map +1 -0
  155. package/dist/spec/render-spec.js +104 -0
  156. package/dist/spec/render-spec.js.map +1 -0
  157. package/dist/spec/resolve.d.ts +39 -0
  158. package/dist/spec/resolve.d.ts.map +1 -0
  159. package/dist/spec/resolve.js +51 -0
  160. package/dist/spec/resolve.js.map +1 -0
  161. package/dist/spec/resolved.d.ts +3 -0
  162. package/dist/spec/resolved.d.ts.map +1 -0
  163. package/dist/spec/resolved.js +36 -0
  164. package/dist/spec/resolved.js.map +1 -0
  165. package/dist/spec/state.d.ts +34 -0
  166. package/dist/spec/state.d.ts.map +1 -0
  167. package/dist/spec/state.js +111 -0
  168. package/dist/spec/state.js.map +1 -0
  169. package/dist/spec/stream.d.ts +50 -0
  170. package/dist/spec/stream.d.ts.map +1 -0
  171. package/dist/spec/stream.js +168 -0
  172. package/dist/spec/stream.js.map +1 -0
  173. package/dist/spec/tools.d.ts +3 -0
  174. package/dist/spec/tools.d.ts.map +1 -0
  175. package/dist/spec/tools.js +3 -0
  176. package/dist/spec/tree.d.ts +21 -0
  177. package/dist/spec/tree.d.ts.map +1 -0
  178. package/dist/spec/tree.js +27 -0
  179. package/dist/spec/tree.js.map +1 -0
  180. package/dist/spec/types.d.ts +66 -0
  181. package/dist/spec/types.d.ts.map +1 -0
  182. package/dist/spec/types.js +9 -0
  183. package/dist/spec/types.js.map +1 -0
  184. package/dist/spec/validate.d.ts +32 -0
  185. package/dist/spec/validate.d.ts.map +1 -0
  186. package/dist/spec/validate.js +264 -0
  187. package/dist/spec/validate.js.map +1 -0
  188. package/dist/spec/vue/SpecRenderer.d.ts +43 -0
  189. package/dist/spec/vue/SpecRenderer.d.ts.map +1 -0
  190. package/dist/spec/vue/SpecRenderer.js +357 -0
  191. package/dist/spec/vue/SpecRenderer.js.map +1 -0
  192. package/dist/spec/vue/useSpecStream.d.ts +19 -0
  193. package/dist/spec/vue/useSpecStream.d.ts.map +1 -0
  194. package/dist/spec/vue/useSpecStream.js +115 -0
  195. package/dist/spec/vue/useSpecStream.js.map +1 -0
  196. package/dist/spec/vue.d.ts +3 -0
  197. package/dist/spec/vue.d.ts.map +1 -0
  198. package/dist/spec/vue.js +3 -0
  199. package/dist/spec.d.ts +12 -0
  200. package/dist/spec.d.ts.map +1 -0
  201. package/dist/spec.js +8 -0
  202. package/dist/theme.d.ts +38 -0
  203. package/dist/theme.d.ts.map +1 -0
  204. package/dist/theme.js +370 -0
  205. package/dist/theme.js.map +1 -0
  206. package/dist/tools/define.d.ts +43 -0
  207. package/dist/tools/define.d.ts.map +1 -0
  208. package/dist/tools/define.js +31 -0
  209. package/dist/tools/define.js.map +1 -0
  210. package/dist/tools/edits.d.ts +19 -0
  211. package/dist/tools/edits.d.ts.map +1 -0
  212. package/dist/tools/edits.js +55 -0
  213. package/dist/tools/edits.js.map +1 -0
  214. package/dist/tools/registry.d.ts +19 -0
  215. package/dist/tools/registry.d.ts.map +1 -0
  216. package/dist/tools/registry.js +33 -0
  217. package/dist/tools/registry.js.map +1 -0
  218. package/dist/tools/tools.d.ts +88 -0
  219. package/dist/tools/tools.d.ts.map +1 -0
  220. package/dist/tools/tools.js +235 -0
  221. package/dist/tools/tools.js.map +1 -0
  222. package/dist/tools.d.ts +5 -0
  223. package/dist/tools.d.ts.map +1 -0
  224. package/dist/tools.js +5 -0
  225. package/dist/version.d.ts +2 -0
  226. package/dist/version.d.ts.map +1 -0
  227. package/dist/version.js +5 -0
  228. package/dist/version.js.map +1 -0
  229. package/dist/vue/Widget.d.ts +16 -0
  230. package/dist/vue/Widget.d.ts.map +1 -0
  231. package/dist/vue/Widget.js +58 -0
  232. package/dist/vue/Widget.js.map +1 -0
  233. package/dist/vue/sync.d.ts +19 -0
  234. package/dist/vue/sync.d.ts.map +1 -0
  235. package/dist/vue/sync.js +31 -0
  236. package/dist/vue/sync.js.map +1 -0
  237. package/dist/vue/themeStore.d.ts +23 -0
  238. package/dist/vue/themeStore.d.ts.map +1 -0
  239. package/dist/vue/themeStore.js +115 -0
  240. package/dist/vue/themeStore.js.map +1 -0
  241. package/dist/vue/useThemeTokens.d.ts +6 -0
  242. package/dist/vue/useThemeTokens.d.ts.map +1 -0
  243. package/dist/vue/useThemeTokens.js +49 -0
  244. package/dist/vue/useThemeTokens.js.map +1 -0
  245. package/dist/vue/useWidget.d.ts +10 -0
  246. package/dist/vue/useWidget.d.ts.map +1 -0
  247. package/dist/vue/useWidget.js +81 -0
  248. package/dist/vue/useWidget.js.map +1 -0
  249. package/dist/vue.d.ts +5 -0
  250. package/dist/vue.d.ts.map +1 -0
  251. package/dist/vue.js +4 -0
  252. package/dist/widget.d.ts +129 -0
  253. package/dist/widget.d.ts.map +1 -0
  254. package/dist/widget.js +655 -0
  255. package/dist/widget.js.map +1 -0
  256. package/package.json +125 -3
  257. package/src/assistant-ui/entry.ts +124 -0
  258. package/src/assistant-ui/extension.ts +19 -0
  259. package/src/assistant-ui/history.ts +63 -0
  260. package/src/assistant-ui/modelContext.ts +162 -0
  261. package/src/assistant-ui/snapshotSaver.ts +116 -0
  262. package/src/assistant-ui/tool-calls.ts +115 -0
  263. package/src/assistant-ui/toolkit.tsx +747 -0
  264. package/src/assistant-ui/useSharedContext.ts +40 -0
  265. package/src/assistant-ui.ts +14 -0
  266. package/src/bootstrap.ts +112 -0
  267. package/src/csp.ts +96 -0
  268. package/src/index.ts +44 -0
  269. package/src/json-schema.ts +353 -0
  270. package/src/preview.ts +120 -0
  271. package/src/prompts/guidance.ts +369 -0
  272. package/src/prompts.ts +8 -0
  273. package/src/protocol.ts +141 -0
  274. package/src/repair/repair.ts +202 -0
  275. package/src/repair.ts +11 -0
  276. package/src/rpc.ts +228 -0
  277. package/src/runtime/diagnostics.ts +165 -0
  278. package/src/runtime/generated.ts +2 -0
  279. package/src/runtime/index.ts +343 -0
  280. package/src/runtime/morph.ts +352 -0
  281. package/src/runtime/screenshot.ts +288 -0
  282. package/src/runtime/size.ts +17 -0
  283. package/src/runtime/storage.ts +49 -0
  284. package/src/spec/actions.ts +111 -0
  285. package/src/spec/assistant-ui/thread.ts +381 -0
  286. package/src/spec/assistant-ui/toolkit.tsx +295 -0
  287. package/src/spec/assistant-ui/web.tsx +63 -0
  288. package/src/spec/assistant-ui.ts +2 -0
  289. package/src/spec/calc.ts +598 -0
  290. package/src/spec/catalog.ts +196 -0
  291. package/src/spec/expressions.ts +301 -0
  292. package/src/spec/patch.ts +112 -0
  293. package/src/spec/pending.ts +36 -0
  294. package/src/spec/pointer.ts +170 -0
  295. package/src/spec/prompt.ts +243 -0
  296. package/src/spec/render-spec.ts +144 -0
  297. package/src/spec/resolve.ts +123 -0
  298. package/src/spec/resolved.ts +53 -0
  299. package/src/spec/state.ts +193 -0
  300. package/src/spec/stream.ts +230 -0
  301. package/src/spec/tools.ts +17 -0
  302. package/src/spec/tree.ts +37 -0
  303. package/src/spec/types.ts +51 -0
  304. package/src/spec/validate.ts +429 -0
  305. package/src/spec/vue/SpecRenderer.tsx +544 -0
  306. package/src/spec/vue/__tests__/harness.ts +50 -0
  307. package/src/spec/vue/useSpecStream.ts +162 -0
  308. package/src/spec/vue.ts +13 -0
  309. package/src/spec.ts +55 -0
  310. package/src/theme.ts +423 -0
  311. package/src/tools/define.ts +65 -0
  312. package/src/tools/edits.ts +73 -0
  313. package/src/tools/registry.ts +44 -0
  314. package/src/tools/tools.ts +361 -0
  315. package/src/tools.ts +32 -0
  316. package/src/version.ts +4 -0
  317. package/src/vue/Widget.tsx +74 -0
  318. package/src/vue/sync.ts +36 -0
  319. package/src/vue/themeStore.ts +157 -0
  320. package/src/vue/useThemeTokens.ts +71 -0
  321. package/src/vue/useWidget.ts +117 -0
  322. package/src/vue.ts +8 -0
  323. package/src/widget.ts +1080 -0
@@ -0,0 +1,65 @@
1
+ import type { JsonSchema } from "../json-schema";
2
+
3
+ export type { JsonSchema };
4
+
5
+ /** A model-facing tool, independent of any provider SDK. */
6
+ export type ToolDefinition<Input, Output> = {
7
+ name: string;
8
+ description: string;
9
+ inputSchema: JsonSchema;
10
+ execute(input: Input): Promise<Output>;
11
+ };
12
+
13
+ export type AnyTool = ToolDefinition<never, unknown>;
14
+
15
+ /** A `read_me` module contributed from outside the built-in set. */
16
+ export type GuidanceModule = {
17
+ name: string;
18
+ /** One line shown in the `read_me` module list. */
19
+ summary: string;
20
+ guidance(): string;
21
+ };
22
+
23
+ /**
24
+ * Converts tools to the Vercel AI SDK shape without depending on it: pass
25
+ * the SDK's own `jsonSchema` helper, e.g.
26
+ * `toAISDKTools(tools, { jsonSchema })` with `import { jsonSchema } from "ai"`.
27
+ */
28
+ export function toAISDKTools<T extends Record<string, AnyTool>, Schema>(
29
+ tools: T,
30
+ sdk: { jsonSchema: (schema: JsonSchema) => Schema },
31
+ ): {
32
+ [K in keyof T]: {
33
+ description: string;
34
+ inputSchema: Schema;
35
+ execute: T[K]["execute"];
36
+ };
37
+ } {
38
+ const result: Record<string, unknown> = {};
39
+ for (const [key, tool] of Object.entries(tools)) {
40
+ result[key] = {
41
+ description: tool.description,
42
+ inputSchema: sdk.jsonSchema(tool.inputSchema),
43
+ execute: (input: never) => tool.execute(input),
44
+ };
45
+ }
46
+ return result as never;
47
+ }
48
+
49
+ /**
50
+ * Each tool's name, description, and input schema without `execute`, for
51
+ * declaring tools to a model where they do not run, e.g. on a server that
52
+ * forwards calls to tools executing in the browser.
53
+ */
54
+ export function getToolDeclarations<T extends Record<string, AnyTool>>(
55
+ tools: T,
56
+ ): { [K in keyof T]: { description: string; inputSchema: JsonSchema } } {
57
+ const result: Record<string, unknown> = {};
58
+ for (const [key, tool] of Object.entries(tools)) {
59
+ result[key] = {
60
+ description: tool.description,
61
+ inputSchema: tool.inputSchema,
62
+ };
63
+ }
64
+ return result as never;
65
+ }
@@ -0,0 +1,73 @@
1
+ export type WidgetEdit = { old_string: string; new_string: string };
2
+
3
+ export type ApplyEditsResult =
4
+ | { ok: true; code: string }
5
+ | { ok: false; error: string; index: number };
6
+
7
+ const countOccurrences = (haystack: string, needle: string) => {
8
+ let count = 0;
9
+ for (
10
+ let at = haystack.indexOf(needle);
11
+ at !== -1;
12
+ at = haystack.indexOf(needle, at + 1)
13
+ ) {
14
+ count++;
15
+ if (count > 1) break;
16
+ }
17
+ return count;
18
+ };
19
+
20
+ const preview = (text: string) =>
21
+ JSON.stringify(text.length > 80 ? `${text.slice(0, 80)}…` : text);
22
+
23
+ /**
24
+ * Applies exact string replacements in order. Each `old_string` must occur
25
+ * exactly once in the code as it stands after the previous edits; on any
26
+ * failure no edit is applied.
27
+ */
28
+ export function applyWidgetEdits(
29
+ code: string,
30
+ edits: readonly WidgetEdit[],
31
+ ): ApplyEditsResult {
32
+ if (edits.length === 0) {
33
+ return {
34
+ ok: false,
35
+ error: "edits must contain at least one replacement",
36
+ index: -1,
37
+ };
38
+ }
39
+ let next = code;
40
+ for (const [index, edit] of edits.entries()) {
41
+ if (!edit.old_string) {
42
+ return { ok: false, error: `edits[${index}].old_string is empty`, index };
43
+ }
44
+ if (edit.old_string === edit.new_string) {
45
+ return {
46
+ ok: false,
47
+ error: `edits[${index}] does not change anything`,
48
+ index,
49
+ };
50
+ }
51
+ const count = countOccurrences(next, edit.old_string);
52
+ if (count === 0) {
53
+ return {
54
+ ok: false,
55
+ error: `edits[${index}].old_string was not found: ${preview(edit.old_string)}. Copy it exactly from the widget's latest code.`,
56
+ index,
57
+ };
58
+ }
59
+ if (count > 1) {
60
+ return {
61
+ ok: false,
62
+ error: `edits[${index}].old_string occurs more than once: ${preview(edit.old_string)}. Include surrounding text so it matches exactly one place.`,
63
+ index,
64
+ };
65
+ }
66
+ const at = next.indexOf(edit.old_string);
67
+ next =
68
+ next.slice(0, at) +
69
+ edit.new_string +
70
+ next.slice(at + edit.old_string.length);
71
+ }
72
+ return { ok: true, code: next };
73
+ }
@@ -0,0 +1,44 @@
1
+ export type WidgetRecord = {
2
+ title: string;
3
+ code: string;
4
+ /** Increments on every show or edit of this title. */
5
+ version: number;
6
+ };
7
+
8
+ export type WidgetRegistry = {
9
+ get(title: string): WidgetRecord | undefined;
10
+ set(title: string, code: string): WidgetRecord;
11
+ delete(title: string): boolean;
12
+ list(): WidgetRecord[];
13
+ subscribe(listener: (record: WidgetRecord) => void): () => void;
14
+ };
15
+
16
+ /** Tracks the latest code of each widget by title, so edits apply to what the user sees. */
17
+ export function createWidgetRegistry(
18
+ initial: Iterable<{ title: string; code: string }> = [],
19
+ ): WidgetRegistry {
20
+ const records = new Map<string, WidgetRecord>();
21
+ const listeners = new Set<(record: WidgetRecord) => void>();
22
+ for (const { title, code } of initial) {
23
+ records.set(title, { title, code, version: 1 });
24
+ }
25
+ return {
26
+ get: (title) => records.get(title),
27
+ set(title, code) {
28
+ const record = {
29
+ title,
30
+ code,
31
+ version: (records.get(title)?.version ?? 0) + 1,
32
+ };
33
+ records.set(title, record);
34
+ for (const listener of listeners) listener(record);
35
+ return record;
36
+ },
37
+ delete: (title) => records.delete(title),
38
+ list: () => [...records.values()],
39
+ subscribe(listener) {
40
+ listeners.add(listener);
41
+ return () => listeners.delete(listener);
42
+ },
43
+ };
44
+ }
@@ -0,0 +1,361 @@
1
+ import type { PreviewResult } from "../preview";
2
+ import {
3
+ buildWidgetGuidance,
4
+ MODULE_SUMMARIES,
5
+ normalizeModules,
6
+ WIDGET_MODULES,
7
+ type GuidanceOptions,
8
+ type Platform,
9
+ type WidgetModule,
10
+ } from "../prompts/guidance";
11
+ import {
12
+ detectWidgetKind,
13
+ type ColorScheme,
14
+ type WidgetKind,
15
+ } from "../protocol";
16
+ import type {
17
+ AnyTool,
18
+ GuidanceModule,
19
+ JsonSchema,
20
+ ToolDefinition,
21
+ } from "./define";
22
+ import { applyWidgetEdits, type WidgetEdit } from "./edits";
23
+ import { createWidgetRegistry, type WidgetRegistry } from "./registry";
24
+
25
+ /** A built-in module, or the name of a module passed in `modules`. */
26
+ export type ReadMeModule = WidgetModule | (string & {});
27
+
28
+ export type ReadMeInput = { modules?: ReadMeModule[]; platform?: Platform };
29
+
30
+ export type ShowWidgetInput = {
31
+ title: string;
32
+ loading_messages?: string[];
33
+ widget_code: string;
34
+ };
35
+
36
+ export type ShowWidgetResult =
37
+ | { ok: true; title: string; kind: WidgetKind; version: number }
38
+ | { ok: false; title: string; error: string };
39
+
40
+ export type EditWidgetInput = { title: string; edits: WidgetEdit[] };
41
+
42
+ export type EditWidgetResult =
43
+ | { ok: true; title: string; version: number; applied: number }
44
+ | { ok: false; title: string; error: string };
45
+
46
+ export type PreviewWidgetInput = {
47
+ widget_code: string;
48
+ width?: number;
49
+ appearance?: ColorScheme;
50
+ };
51
+
52
+ export type WidgetTools = {
53
+ read_me: ToolDefinition<ReadMeInput, string>;
54
+ show_widget: ToolDefinition<ShowWidgetInput, ShowWidgetResult>;
55
+ edit_widget: ToolDefinition<EditWidgetInput, EditWidgetResult>;
56
+ preview_widget: ToolDefinition<PreviewWidgetInput, PreviewResult>;
57
+ };
58
+
59
+ export type CreateWidgetToolsOptions = {
60
+ registry?: WidgetRegistry;
61
+ /** Guidance options other than modules and platform, which the model picks. */
62
+ guidance?: Omit<GuidanceOptions, "modules" | "platform">;
63
+ /** Renders code offscreen for `preview_widget`, typically `previewWidget` from the core entry. */
64
+ preview?: (
65
+ code: string,
66
+ options: { width?: number; appearance?: ColorScheme },
67
+ ) => Promise<PreviewResult>;
68
+ /** Extra `read_me` modules, such as `specGuidanceModule(catalog)` from `generative-frame/spec/tools`. */
69
+ modules?: readonly GuidanceModule[];
70
+ };
71
+
72
+ const TITLE_SCHEMA: JsonSchema = {
73
+ type: "string",
74
+ description:
75
+ "Short snake_case identifier for the widget, unique in this conversation (e.g. `q3_revenue_by_region`). Reuse it to replace or edit the widget.",
76
+ };
77
+
78
+ /**
79
+ * Model-facing tools for widgets, independent of any provider SDK: each has
80
+ * a name, a description, a JSON Schema for its input, and `execute`.
81
+ * `extraTools` are added as they are (for example `createSpecTools(catalog)`
82
+ * from `generative-frame/spec/tools`), and `modules` extend `read_me`.
83
+ */
84
+ export function createWidgetTools<
85
+ Extra extends Record<string, AnyTool> = Record<never, never>,
86
+ >(
87
+ options: CreateWidgetToolsOptions & { extraTools?: Extra } = {},
88
+ ): WidgetTools & Extra {
89
+ const registry = options.registry ?? createWidgetRegistry();
90
+ const extraModules = options.modules ?? [];
91
+ const moduleNames: readonly string[] = [
92
+ ...WIDGET_MODULES,
93
+ ...extraModules.map((m) => m.name),
94
+ ];
95
+ const moduleDocs = [
96
+ ...WIDGET_MODULES.map((m) => `${m} (${MODULE_SUMMARIES[m]})`),
97
+ ...extraModules.map((m) => `${m.name} (${m.summary})`),
98
+ ];
99
+
100
+ const tools: WidgetTools = {
101
+ read_me: {
102
+ name: "read_me",
103
+ description:
104
+ "Returns the rules for writing widgets plus guidance for the requested modules. Call it silently before your first `show_widget` call, and again when you need a module whose guidance you do not have yet. Do not mention this call to the user.",
105
+ inputSchema: {
106
+ type: "object",
107
+ properties: {
108
+ modules: {
109
+ type: "array",
110
+ description: `Modules to load: ${moduleDocs.join("; ")}.`,
111
+ items: { type: "string", enum: moduleNames },
112
+ },
113
+ platform: {
114
+ type: "string",
115
+ enum: ["desktop", "mobile"],
116
+ description: "The user's client. Defaults to desktop.",
117
+ },
118
+ },
119
+ additionalProperties: false,
120
+ },
121
+ async execute(input) {
122
+ const requested: readonly string[] = input.modules ?? [];
123
+ const extra = extraModules
124
+ .filter((m) => requested.includes(m.name))
125
+ .map((m) => m.guidance())
126
+ .join("\n\n");
127
+ const modules = normalizeModules(requested);
128
+ if (extra && modules.length === 0) return extra;
129
+ const guidance = buildWidgetGuidance({
130
+ ...options.guidance,
131
+ modules,
132
+ platform: input.platform === "mobile" ? "mobile" : "desktop",
133
+ });
134
+ return extra ? `${guidance}\n\n${extra}` : guidance;
135
+ },
136
+ },
137
+
138
+ show_widget: {
139
+ name: "show_widget",
140
+ description:
141
+ "Shows a visual widget (HTML fragment or SVG) inline in the conversation, streaming it as you write. Write a short <style> first, then markup, then scripts. Call read_me first. Showing a title that already exists replaces that widget.",
142
+ inputSchema: {
143
+ type: "object",
144
+ properties: {
145
+ title: TITLE_SCHEMA,
146
+ loading_messages: {
147
+ type: "array",
148
+ description:
149
+ "One to four short status lines shown while the widget streams.",
150
+ items: { type: "string" },
151
+ maxItems: 4,
152
+ },
153
+ widget_code: {
154
+ type: "string",
155
+ description:
156
+ "The widget: an HTML fragment (no doctype, html, head, or body), or code starting with <svg for a standalone SVG.",
157
+ },
158
+ },
159
+ required: ["title", "widget_code"],
160
+ additionalProperties: false,
161
+ },
162
+ async execute(input) {
163
+ if (!input.widget_code?.trim()) {
164
+ return {
165
+ ok: false,
166
+ title: input.title,
167
+ error: "widget_code is empty",
168
+ };
169
+ }
170
+ const record = registry.set(input.title, input.widget_code);
171
+ return {
172
+ ok: true,
173
+ title: input.title,
174
+ kind: detectWidgetKind(input.widget_code),
175
+ version: record.version,
176
+ };
177
+ },
178
+ },
179
+
180
+ edit_widget: {
181
+ name: "edit_widget",
182
+ description:
183
+ "Changes a widget you already showed with exact string replacements, applied in order to its latest code. Each old_string must match exactly one place; include surrounding text to make it unique. No edit is applied if any one fails.",
184
+ inputSchema: {
185
+ type: "object",
186
+ properties: {
187
+ title: TITLE_SCHEMA,
188
+ edits: {
189
+ type: "array",
190
+ minItems: 1,
191
+ items: {
192
+ type: "object",
193
+ properties: {
194
+ old_string: {
195
+ type: "string",
196
+ description:
197
+ "Exact text to find, copied from the widget's latest code.",
198
+ },
199
+ new_string: {
200
+ type: "string",
201
+ description: "Replacement text.",
202
+ },
203
+ },
204
+ required: ["old_string", "new_string"],
205
+ additionalProperties: false,
206
+ },
207
+ },
208
+ },
209
+ required: ["title", "edits"],
210
+ additionalProperties: false,
211
+ },
212
+ async execute(input) {
213
+ const record = registry.get(input.title);
214
+ if (!record) {
215
+ const known = registry.list().map((r) => r.title);
216
+ return {
217
+ ok: false,
218
+ title: input.title,
219
+ error: `No widget titled "${input.title}".${
220
+ known.length > 0 ? ` Known widgets: ${known.join(", ")}.` : ""
221
+ } Use show_widget to create it.`,
222
+ };
223
+ }
224
+ const result = applyWidgetEdits(record.code, input.edits ?? []);
225
+ if (!result.ok)
226
+ return { ok: false, title: input.title, error: result.error };
227
+ const updated = registry.set(input.title, result.code);
228
+ return {
229
+ ok: true,
230
+ title: input.title,
231
+ version: updated.version,
232
+ applied: input.edits.length,
233
+ };
234
+ },
235
+ },
236
+
237
+ preview_widget: {
238
+ name: "preview_widget",
239
+ description:
240
+ "Renders widget code offscreen without showing it and reports errors, console output, the rendered height, whether it came out blank, and a screenshot. Use it to check complex widgets before showing them.",
241
+ inputSchema: {
242
+ type: "object",
243
+ properties: {
244
+ widget_code: { type: "string", description: "Complete widget code." },
245
+ width: {
246
+ type: "number",
247
+ description: "Layout width in CSS pixels. Defaults to 680.",
248
+ minimum: 240,
249
+ maximum: 1600,
250
+ },
251
+ appearance: {
252
+ type: "string",
253
+ enum: ["light", "dark"],
254
+ description: "Color scheme to render in. Defaults to light.",
255
+ },
256
+ },
257
+ required: ["widget_code"],
258
+ additionalProperties: false,
259
+ },
260
+ async execute(input) {
261
+ if (!options.preview) {
262
+ return {
263
+ ok: false,
264
+ kind: detectWidgetKind(input.widget_code),
265
+ width: 0,
266
+ height: 0,
267
+ blank: true,
268
+ errors: [
269
+ {
270
+ kind: "error",
271
+ message: "Previews are not available in this environment.",
272
+ },
273
+ ],
274
+ console: [],
275
+ };
276
+ }
277
+ return options.preview(input.widget_code, {
278
+ ...(input.width !== undefined ? { width: input.width } : {}),
279
+ ...(input.appearance !== undefined
280
+ ? { appearance: input.appearance }
281
+ : {}),
282
+ });
283
+ },
284
+ },
285
+ };
286
+
287
+ return { ...tools, ...options.extraTools } as WidgetTools & Extra;
288
+ }
289
+
290
+ export type WidgetInstructionsOptions = {
291
+ /**
292
+ * Inline `read_me` output for these modules into the instructions, which
293
+ * saves the model a round trip at the cost of a larger system prompt.
294
+ */
295
+ preload?: ReadMeInput;
296
+ };
297
+
298
+ /** Whether `read_me` lists a module, which is how composed modules become visible. */
299
+ const readMeOffers = (tools: WidgetTools, module: string) =>
300
+ (
301
+ tools.read_me.inputSchema.properties?.["modules"]?.items?.enum ?? []
302
+ ).includes(module);
303
+
304
+ const WIDGET_TOOL_RULES = [
305
+ "- Call `read_me` once, silently, before your first `show_widget` call, with the modules you need.",
306
+ "- Change an existing widget with `edit_widget` (exact string replacements) instead of re-sending it.",
307
+ ];
308
+
309
+ /**
310
+ * A short system prompt section telling the model when and how to use the
311
+ * widget tools, optionally with `read_me` guidance preloaded. With
312
+ * `render_spec` among the tools, it also tells the model when to answer in
313
+ * Markdown, with a spec, or with a widget.
314
+ */
315
+ export async function buildWidgetInstructions(
316
+ tools: WidgetTools & Record<string, AnyTool>,
317
+ options: WidgetInstructionsOptions = {},
318
+ ): Promise<string> {
319
+ const lines =
320
+ "render_spec" in tools
321
+ ? [
322
+ "## Choosing how to answer",
323
+ "",
324
+ "Use the lightest format that fully answers the request:",
325
+ "",
326
+ "- Markdown, with no tool call, for explanations, advice, short answers, and anything read once. Most answers are text.",
327
+ "- `render_spec` for structured content the user scans or acts on, built from the app's components: comparisons, step-by-step guides, plans and itineraries, summaries of facts, and forms or choices. A spec can show values its `$calc` formulas compute from state, such as totals or quantities that follow a guest count; a tool whose point is the calculation, such as a calculator, is a widget.",
328
+ "- `show_widget` for interactive tools that calculate as the user changes inputs, such as calculators, simulations, and games, and for visuals the components cannot draw.",
329
+ "",
330
+ "Do not render a paragraph or a short list. Around a rendered answer, keep prose to a sentence or two and do not repeat what it shows.",
331
+ "",
332
+ "The Declarative UI guidance describes what goes inside a `render_spec` call; its output rules apply to those patches, not to your answer.",
333
+ "",
334
+ "## Widgets",
335
+ "",
336
+ ...WIDGET_TOOL_RULES,
337
+ `- The ${WIDGET_MODULES.map((module) => `\`${module}\``).join(", ")} modules of \`read_me\` guide \`show_widget\` code, so a \`render_spec\` answer never needs them.`,
338
+ ...(readMeOffers(tools, "spec") &&
339
+ !options.preload?.modules?.includes("spec")
340
+ ? [
341
+ "- Load the `spec` module with `read_me` before your first `render_spec` call.",
342
+ ]
343
+ : []),
344
+ ]
345
+ : [
346
+ "## Visual widgets",
347
+ "",
348
+ "When a visual would explain better than text (charts, diagrams, comparisons, small interactive tools, forms), show it with `show_widget` instead of describing it. Keep surrounding prose short and do not repeat what the widget shows.",
349
+ ...WIDGET_TOOL_RULES,
350
+ ];
351
+ const preloaded = options.preload?.modules ?? [];
352
+ if (preloaded.length) {
353
+ lines.push(
354
+ `- The guidance for ${preloaded.map((module) => `\`${module}\``).join(", ")} follows, so never load it with \`read_me\`.`,
355
+ );
356
+ }
357
+ if (options.preload) {
358
+ lines.push("", await tools.read_me.execute(options.preload));
359
+ }
360
+ return lines.join("\n");
361
+ }
package/src/tools.ts ADDED
@@ -0,0 +1,32 @@
1
+ export {
2
+ buildWidgetInstructions,
3
+ createWidgetTools,
4
+ type CreateWidgetToolsOptions,
5
+ type EditWidgetInput,
6
+ type EditWidgetResult,
7
+ type PreviewWidgetInput,
8
+ type ReadMeInput,
9
+ type ReadMeModule,
10
+ type WidgetInstructionsOptions,
11
+ type ShowWidgetInput,
12
+ type ShowWidgetResult,
13
+ type WidgetTools,
14
+ } from "./tools/tools";
15
+ export {
16
+ getToolDeclarations,
17
+ toAISDKTools,
18
+ type AnyTool,
19
+ type GuidanceModule,
20
+ type JsonSchema,
21
+ type ToolDefinition,
22
+ } from "./tools/define";
23
+ export {
24
+ applyWidgetEdits,
25
+ type ApplyEditsResult,
26
+ type WidgetEdit,
27
+ } from "./tools/edits";
28
+ export {
29
+ createWidgetRegistry,
30
+ type WidgetRecord,
31
+ type WidgetRegistry,
32
+ } from "./tools/registry";
package/src/version.ts ADDED
@@ -0,0 +1,4 @@
1
+ export const VERSION =
2
+ typeof __AUI_PACKAGE_VERSION__ === "string"
3
+ ? __AUI_PACKAGE_VERSION__
4
+ : "0.0.0";
@@ -0,0 +1,74 @@
1
+ import { defineComponent, h, watchEffect, type CSSProperties } from "vue";
2
+ import { syncWidgetCode } from "./sync";
3
+ import { useWidget, type UseWidgetOptions } from "./useWidget";
4
+ export type WidgetProps = UseWidgetOptions & {
5
+ code: string;
6
+ streaming?: boolean;
7
+ className?: string;
8
+ style?: CSSProperties;
9
+ };
10
+ /** Renders model-generated HTML or SVG in a Safe Content Frame, streaming as code grows. */
11
+ export const Widget = defineComponent(
12
+ (props: WidgetProps, { attrs }) => {
13
+ const { ref, widget } = useWidget(() => {
14
+ const {
15
+ code: _code,
16
+ streaming: _streaming,
17
+ className: _className,
18
+ style: _style,
19
+ ...options
20
+ } = props;
21
+ return options;
22
+ });
23
+ watchEffect(
24
+ () => {
25
+ if (widget.value)
26
+ syncWidgetCode(widget.value, props.code, props.streaming ?? false);
27
+ },
28
+ { flush: "post" },
29
+ );
30
+ return () =>
31
+ h("div", {
32
+ ...attrs,
33
+ ref,
34
+ class: [attrs.class, props.className],
35
+ style: props.style,
36
+ });
37
+ },
38
+ {
39
+ name: "Widget",
40
+ inheritAttrs: false,
41
+ props: [
42
+ "code",
43
+ "streaming",
44
+ "className",
45
+ "style",
46
+ "tokens",
47
+ "initialState",
48
+ "csp",
49
+ "product",
50
+ "maxHeight",
51
+ "id",
52
+ "unsafeShimDomain",
53
+ "frame",
54
+ "opaqueOrigin",
55
+ "context",
56
+ "animate",
57
+ "css",
58
+ "minHeight",
59
+ "readyTimeoutMs",
60
+ "onPrompt",
61
+ "onMessage",
62
+ "onOpenLink",
63
+ "onCallTool",
64
+ "onInitialized",
65
+ "onRequestDisplayMode",
66
+ "onUpdateModelContext",
67
+ "onCopyText",
68
+ "onWidgetState",
69
+ "onResize",
70
+ "onError",
71
+ "onLog",
72
+ ],
73
+ },
74
+ );
@@ -0,0 +1,36 @@
1
+ import type { WidgetHandle } from "../widget";
2
+
3
+ export type CodeUpdate =
4
+ | { type: "none" }
5
+ | { type: "append"; chunk: string }
6
+ | { type: "replace"; code: string };
7
+
8
+ /** Decides how to move a widget from the code it has to `next`. */
9
+ export function planCodeUpdate(current: string, next: string): CodeUpdate {
10
+ if (next === current) return { type: "none" };
11
+ if (next.startsWith(current)) {
12
+ return { type: "append", chunk: next.slice(current.length) };
13
+ }
14
+ return { type: "replace", code: next };
15
+ }
16
+
17
+ const ignore = () => {};
18
+
19
+ /**
20
+ * Brings a widget in line with a code prop that is either complete or still
21
+ * growing: only the new suffix is written while it grows, `end` runs once it
22
+ * stops, and anything that is not an extension replaces the widget's code.
23
+ */
24
+ export function syncWidgetCode(
25
+ widget: Pick<WidgetHandle, "code" | "ended" | "write" | "end" | "replace">,
26
+ code: string,
27
+ streaming: boolean,
28
+ ): void {
29
+ const plan = planCodeUpdate(widget.code, code);
30
+ if (widget.ended || plan.type === "replace") {
31
+ if (plan.type !== "none") void widget.replace(code).catch(ignore);
32
+ return;
33
+ }
34
+ if (plan.type === "append") widget.write(plan.chunk);
35
+ if (!streaming && code.length > 0) void widget.end().catch(ignore);
36
+ }