@fraza/module-ai 0.0.0-stage → 0.4.23

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 (54) hide show
  1. package/CHANGELOG.md +158 -0
  2. package/LICENSE +21 -0
  3. package/README.md +72 -2
  4. package/THIRD-PARTY-NOTICES.md +11 -0
  5. package/changelog.json +478 -0
  6. package/dist/admin/AiActions.client.js +166 -0
  7. package/dist/admin/AiChoices.client.js +67 -0
  8. package/dist/admin/AiInputForm.client.js +57 -0
  9. package/dist/admin/AiPage.client.js +181 -0
  10. package/dist/admin/AiResults.client.js +76 -0
  11. package/dist/admin/AiStudio.client.js +453 -0
  12. package/dist/admin/AiUsageWidget.client.js +28 -0
  13. package/dist/admin/StudioBar.client.js +33 -0
  14. package/dist/admin/StudioCompose.client.js +457 -0
  15. package/dist/admin/StudioComposer.client.js +217 -0
  16. package/dist/admin/StudioFields.client.js +124 -0
  17. package/dist/admin/StudioPlan.client.js +94 -0
  18. package/dist/admin/StudioWorking.client.js +157 -0
  19. package/dist/admin/choices.js +22 -0
  20. package/dist/admin/format.js +19 -0
  21. package/dist/admin/input-form.js +59 -0
  22. package/dist/admin/paths.js +4 -0
  23. package/dist/admin/studio-compose.js +94 -0
  24. package/dist/admin/studio-diff.js +240 -0
  25. package/dist/admin/widgets.js +14 -0
  26. package/dist/admin.js +9 -0
  27. package/dist/api.server.js +155 -0
  28. package/dist/config.server.js +113 -0
  29. package/dist/cron.server.js +26 -0
  30. package/dist/db.server.js +65 -0
  31. package/dist/index.js +43 -0
  32. package/dist/integration.server.js +28 -0
  33. package/dist/migrations.js +4 -0
  34. package/dist/permissions.js +9 -0
  35. package/dist/prompts.server.js +149 -0
  36. package/dist/providers/anthropic.server.js +42 -0
  37. package/dist/providers/chat-completions.server.js +63 -0
  38. package/dist/providers/google.server.js +52 -0
  39. package/dist/providers/http.server.js +98 -0
  40. package/dist/providers/index.server.js +22 -0
  41. package/dist/providers/ollama.server.js +45 -0
  42. package/dist/register-server.js +34 -0
  43. package/dist/results.server.js +74 -0
  44. package/dist/run.server.js +166 -0
  45. package/dist/runs.server.js +106 -0
  46. package/dist/schema.js +190 -0
  47. package/dist/seed.server.js +205 -0
  48. package/dist/server.js +19 -0
  49. package/dist/source.js +4 -0
  50. package/dist/studio.js +205 -0
  51. package/dist/test-prompt.server.js +24 -0
  52. package/dist/version.js +4 -0
  53. package/dist/widgets.server.js +29 -0
  54. package/package.json +67 -4
package/changelog.json ADDED
@@ -0,0 +1,478 @@
1
+ {
2
+ "name": "@fraza/module-ai",
3
+ "versions": [
4
+ {
5
+ "version": "0.4.23",
6
+ "date": "2026-11-08",
7
+ "breaking": [],
8
+ "envAdded": [],
9
+ "envRemoved": [],
10
+ "envRenamed": [],
11
+ "templateChanged": [],
12
+ "action": [],
13
+ "entries": [
14
+ {
15
+ "type": "feat",
16
+ "scope": "ai",
17
+ "subject": "add demo history seed for fraza-db",
18
+ "hash": "83306d4",
19
+ "breaking": false
20
+ }
21
+ ]
22
+ },
23
+ {
24
+ "version": "0.4.22",
25
+ "date": "2026-11-07",
26
+ "breaking": [],
27
+ "envAdded": [],
28
+ "envRemoved": [],
29
+ "envRenamed": [],
30
+ "templateChanged": [],
31
+ "action": [],
32
+ "entries": []
33
+ },
34
+ {
35
+ "version": "0.4.21",
36
+ "date": "2026-11-07",
37
+ "breaking": [],
38
+ "envAdded": [],
39
+ "envRemoved": [],
40
+ "envRenamed": [],
41
+ "templateChanged": [],
42
+ "action": [],
43
+ "entries": [
44
+ {
45
+ "type": "feat",
46
+ "scope": "ui",
47
+ "subject": "add animated working status to ai studio",
48
+ "hash": "92e1667",
49
+ "breaking": false
50
+ }
51
+ ]
52
+ },
53
+ {
54
+ "version": "0.4.20",
55
+ "date": "2026-11-07",
56
+ "breaking": [],
57
+ "envAdded": [],
58
+ "envRemoved": [],
59
+ "envRenamed": [],
60
+ "templateChanged": [],
61
+ "action": [],
62
+ "entries": [
63
+ {
64
+ "type": "style",
65
+ "scope": "ui",
66
+ "subject": "move studio composer focus light inside the box",
67
+ "hash": "78f9481",
68
+ "breaking": false
69
+ }
70
+ ]
71
+ },
72
+ {
73
+ "version": "0.4.19",
74
+ "date": "2026-11-07",
75
+ "breaking": [],
76
+ "envAdded": [],
77
+ "envRemoved": [],
78
+ "envRenamed": [],
79
+ "templateChanged": [],
80
+ "action": [],
81
+ "entries": [
82
+ {
83
+ "type": "style",
84
+ "scope": "ui",
85
+ "subject": "soften studio composer focus glow and grain",
86
+ "hash": "b93cb0d",
87
+ "breaking": false
88
+ }
89
+ ]
90
+ },
91
+ {
92
+ "version": "0.4.18",
93
+ "date": "2026-11-07",
94
+ "breaking": [],
95
+ "envAdded": [],
96
+ "envRemoved": [],
97
+ "envRenamed": [],
98
+ "templateChanged": [],
99
+ "action": [],
100
+ "entries": [
101
+ {
102
+ "type": "style",
103
+ "scope": "ui",
104
+ "subject": "reshape studio composer glow to match the box",
105
+ "hash": "d54e21a",
106
+ "breaking": false
107
+ }
108
+ ]
109
+ },
110
+ {
111
+ "version": "0.4.17",
112
+ "date": "2026-11-07",
113
+ "breaking": [],
114
+ "envAdded": [],
115
+ "envRemoved": [],
116
+ "envRenamed": [],
117
+ "templateChanged": [],
118
+ "action": [],
119
+ "entries": [
120
+ {
121
+ "type": "style",
122
+ "scope": "ui",
123
+ "subject": "soften studio composer focus glow",
124
+ "hash": "7693879",
125
+ "breaking": false
126
+ }
127
+ ]
128
+ },
129
+ {
130
+ "version": "0.4.16",
131
+ "date": "2026-11-07",
132
+ "breaking": [],
133
+ "envAdded": [],
134
+ "envRemoved": [],
135
+ "envRenamed": [],
136
+ "templateChanged": [],
137
+ "action": [],
138
+ "entries": [
139
+ {
140
+ "type": "style",
141
+ "scope": "ui",
142
+ "subject": "soften studio composer glow and shadow",
143
+ "hash": "88946a9",
144
+ "breaking": false
145
+ }
146
+ ]
147
+ },
148
+ {
149
+ "version": "0.4.15",
150
+ "date": "2026-11-07",
151
+ "breaking": [],
152
+ "envAdded": [],
153
+ "envRemoved": [],
154
+ "envRenamed": [],
155
+ "templateChanged": [],
156
+ "action": [],
157
+ "entries": [
158
+ {
159
+ "type": "feat",
160
+ "scope": "admin",
161
+ "subject": "add start mode panel to studio composer",
162
+ "hash": "2cbd1f6",
163
+ "breaking": false
164
+ }
165
+ ]
166
+ },
167
+ {
168
+ "version": "0.4.14",
169
+ "date": "2026-11-07",
170
+ "breaking": [],
171
+ "envAdded": [],
172
+ "envRemoved": [],
173
+ "envRenamed": [],
174
+ "templateChanged": [],
175
+ "action": [],
176
+ "entries": [
177
+ {
178
+ "type": "refactor",
179
+ "scope": "ai",
180
+ "subject": "extract studio composer into its own component",
181
+ "hash": "4e74444",
182
+ "breaking": false
183
+ }
184
+ ]
185
+ },
186
+ {
187
+ "version": "0.4.13",
188
+ "date": "2026-11-07",
189
+ "breaking": [],
190
+ "envAdded": [],
191
+ "envRemoved": [],
192
+ "envRenamed": [],
193
+ "templateChanged": [],
194
+ "action": [],
195
+ "entries": [
196
+ {
197
+ "type": "style",
198
+ "scope": "ui",
199
+ "subject": "add bottom border to studio label bar",
200
+ "hash": "fc4d004",
201
+ "breaking": false
202
+ }
203
+ ]
204
+ },
205
+ {
206
+ "version": "0.4.12",
207
+ "date": "2026-11-07",
208
+ "breaking": [],
209
+ "envAdded": [],
210
+ "envRemoved": [],
211
+ "envRenamed": [],
212
+ "templateChanged": [],
213
+ "action": [],
214
+ "entries": [
215
+ {
216
+ "type": "refactor",
217
+ "scope": "admin",
218
+ "subject": "move studio bar and window layout into shared component",
219
+ "hash": "ea95fdf",
220
+ "breaking": false
221
+ }
222
+ ]
223
+ },
224
+ {
225
+ "version": "0.4.11",
226
+ "date": "2026-11-07",
227
+ "breaking": [],
228
+ "envAdded": [],
229
+ "envRemoved": [],
230
+ "envRenamed": [],
231
+ "templateChanged": [],
232
+ "action": [],
233
+ "entries": [
234
+ {
235
+ "type": "feat",
236
+ "scope": "studio",
237
+ "subject": "group hints into menus and animate the compose layout",
238
+ "hash": "662ef1c",
239
+ "breaking": false
240
+ }
241
+ ]
242
+ },
243
+ {
244
+ "version": "0.4.10",
245
+ "date": "2026-11-07",
246
+ "breaking": [],
247
+ "envAdded": [],
248
+ "envRemoved": [],
249
+ "envRenamed": [],
250
+ "templateChanged": [],
251
+ "action": [],
252
+ "entries": [
253
+ {
254
+ "type": "feat",
255
+ "scope": "ai",
256
+ "subject": "add studio compose mode and plan review",
257
+ "hash": "46ded16",
258
+ "breaking": false
259
+ }
260
+ ]
261
+ },
262
+ {
263
+ "version": "0.4.9",
264
+ "date": "2026-11-07",
265
+ "breaking": [],
266
+ "envAdded": [],
267
+ "envRemoved": [],
268
+ "envRenamed": [],
269
+ "templateChanged": [],
270
+ "action": [],
271
+ "entries": [
272
+ {
273
+ "type": "refactor",
274
+ "scope": "admin",
275
+ "subject": "rename ai sparkles icon to sparkles icon",
276
+ "hash": "3ccf3f6",
277
+ "breaking": false
278
+ },
279
+ {
280
+ "type": "feat",
281
+ "scope": "admin",
282
+ "subject": "add icon-only and size options to ai actions trigger",
283
+ "hash": "eaa0877",
284
+ "breaking": false
285
+ }
286
+ ]
287
+ },
288
+ {
289
+ "version": "0.4.8",
290
+ "date": "2026-11-07",
291
+ "breaking": [],
292
+ "envAdded": [],
293
+ "envRemoved": [],
294
+ "envRenamed": [],
295
+ "templateChanged": [],
296
+ "action": [],
297
+ "entries": [
298
+ {
299
+ "type": "refactor",
300
+ "scope": "ui",
301
+ "subject": "replace ChatSparkIcon with AiSparklesIcon",
302
+ "hash": "1dd3dee",
303
+ "breaking": false
304
+ }
305
+ ]
306
+ },
307
+ {
308
+ "version": "0.4.7",
309
+ "date": "2026-11-07",
310
+ "breaking": [],
311
+ "envAdded": [],
312
+ "envRemoved": [],
313
+ "envRenamed": [],
314
+ "templateChanged": [],
315
+ "action": [],
316
+ "entries": [
317
+ {
318
+ "type": "refactor",
319
+ "scope": "ui",
320
+ "subject": "rename ai apply wording to \"appliquer\" and \"laisser tel quel\"",
321
+ "hash": "471087a",
322
+ "breaking": false
323
+ }
324
+ ]
325
+ },
326
+ {
327
+ "version": "0.4.6",
328
+ "date": "2026-11-07",
329
+ "breaking": [],
330
+ "envAdded": [],
331
+ "envRemoved": [],
332
+ "envRenamed": [],
333
+ "templateChanged": [],
334
+ "action": [],
335
+ "entries": [
336
+ {
337
+ "type": "feat",
338
+ "scope": "ai",
339
+ "subject": "add studio mode for comparing and applying prompt versions",
340
+ "hash": "d2b427d",
341
+ "breaking": false
342
+ }
343
+ ]
344
+ },
345
+ {
346
+ "version": "0.4.5",
347
+ "date": "2026-11-07",
348
+ "breaking": [],
349
+ "envAdded": [],
350
+ "envRemoved": [],
351
+ "envRenamed": [],
352
+ "templateChanged": [],
353
+ "action": [],
354
+ "entries": [
355
+ {
356
+ "type": "feat",
357
+ "scope": "admin",
358
+ "subject": "enlarge and make resizable long text inputs in ai form",
359
+ "hash": "858b23c",
360
+ "breaking": false
361
+ }
362
+ ]
363
+ },
364
+ {
365
+ "version": "0.4.4",
366
+ "date": "2026-11-07",
367
+ "breaking": [],
368
+ "envAdded": [],
369
+ "envRemoved": [],
370
+ "envRenamed": [],
371
+ "templateChanged": [],
372
+ "action": [],
373
+ "entries": [
374
+ {
375
+ "type": "feat",
376
+ "scope": "ai",
377
+ "subject": "add configurable menu label with action descriptions",
378
+ "hash": "526a6f7",
379
+ "breaking": false
380
+ }
381
+ ]
382
+ },
383
+ {
384
+ "version": "0.4.3",
385
+ "date": "2026-11-07",
386
+ "breaking": [],
387
+ "envAdded": [],
388
+ "envRemoved": [],
389
+ "envRenamed": [],
390
+ "templateChanged": [],
391
+ "action": [],
392
+ "entries": [
393
+ {
394
+ "type": "refactor",
395
+ "scope": "admin",
396
+ "subject": "use ai trigger component for single action button",
397
+ "hash": "b79c212",
398
+ "breaking": false
399
+ }
400
+ ]
401
+ },
402
+ {
403
+ "version": "0.4.2",
404
+ "date": "2026-11-07",
405
+ "breaking": [],
406
+ "envAdded": [],
407
+ "envRemoved": [],
408
+ "envRenamed": [],
409
+ "templateChanged": [],
410
+ "action": [],
411
+ "entries": [
412
+ {
413
+ "type": "feat",
414
+ "scope": "ai",
415
+ "subject": "redesign ai modal with preview, steps and eyebrow",
416
+ "hash": "ec4ca14",
417
+ "breaking": false
418
+ }
419
+ ]
420
+ },
421
+ {
422
+ "version": "0.4.1",
423
+ "date": "2026-11-07",
424
+ "breaking": [],
425
+ "envAdded": [],
426
+ "envRemoved": [],
427
+ "envRenamed": [],
428
+ "templateChanged": [],
429
+ "action": [],
430
+ "entries": [
431
+ {
432
+ "type": "refactor",
433
+ "scope": "ai",
434
+ "subject": "use core ai design system in admin actions",
435
+ "hash": "44cf966",
436
+ "breaking": false
437
+ }
438
+ ]
439
+ },
440
+ {
441
+ "version": "0.4.0",
442
+ "date": "2026-11-07",
443
+ "breaking": [],
444
+ "envAdded": [
445
+ "FRAZA_MODULE_AI",
446
+ "FRAZA_MODULE_AI_API_KEY",
447
+ "FRAZA_MODULE_AI_BASE_URL",
448
+ "FRAZA_MODULE_AI_MODEL",
449
+ "FRAZA_MODULE_AI_PROVIDER",
450
+ "FRAZA_MODULE_AI_RETENTION_DAYS",
451
+ "FRAZA_MODULE_AI_TIMEOUT_MS"
452
+ ],
453
+ "envRemoved": [],
454
+ "envRenamed": [],
455
+ "templateChanged": [],
456
+ "action": [
457
+ "npx fraza-db migrate",
458
+ "npx fraza-doctor --fix"
459
+ ],
460
+ "entries": [
461
+ {
462
+ "type": "feat",
463
+ "scope": null,
464
+ "subject": "add ai module with providers, admin pages and prompts",
465
+ "hash": "6c49c51",
466
+ "breaking": false
467
+ },
468
+ {
469
+ "type": "other",
470
+ "scope": null,
471
+ "subject": "Initial commit",
472
+ "hash": "6240154",
473
+ "breaking": false
474
+ }
475
+ ]
476
+ }
477
+ ]
478
+ }
@@ -0,0 +1,166 @@
1
+ "use client";
2
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
+ import React, { useCallback, useEffect, useId, useMemo, useState } from "react";
4
+ import { AiPreview, AiTrigger, AiWorking, Button, Modal } from "@fraza/core/shared/components";
5
+ import { apiFetch } from "@fraza/core/shared/http";
6
+ import { useToast } from "@fraza/core/toast";
7
+ import { SparklesIcon } from "@fraza/icons/SparklesIcon";
8
+ import AiChoices from "./AiChoices.client.js";
9
+ import AiInputForm from "./AiInputForm.client.js";
10
+ import AiStudio from "./AiStudio.client.js";
11
+ import { applyChoices } from "./choices.js";
12
+ import { formFields, initialValues, readValues } from "./input-form.js";
13
+ import { AI_API } from "./paths.js";
14
+ function useActions(target, scopeKey) {
15
+ const [actions, setActions] = useState([]);
16
+ useEffect(() => {
17
+ let cancelled = false;
18
+ const query = new URLSearchParams({ target, scope: scopeKey });
19
+ apiFetch(`${AI_API}/actions?${query}`).then((data) => {
20
+ if (!cancelled) setActions((data == null ? void 0 : data.configured) ? data.actions ?? [] : []);
21
+ }).catch(() => {
22
+ if (!cancelled) setActions([]);
23
+ });
24
+ return () => {
25
+ cancelled = true;
26
+ };
27
+ }, [target, scopeKey]);
28
+ return actions;
29
+ }
30
+ function stepsOf(output) {
31
+ return Object.entries((output == null ? void 0 : output.properties) ?? {}).map(([key, node]) => node.title ?? key);
32
+ }
33
+ const KEEP_NOTE = "Rien n'est enregistr\xE9 avant la relecture.";
34
+ function AiActions({ target, scope = {}, context, current, onApply, disabled = false, preview, menuLabel = "Actions d'IA", iconOnly = false, size }) {
35
+ const toast = useToast();
36
+ const formId = useId();
37
+ const scopeKey = useMemo(() => JSON.stringify(scope ?? {}), [scope]);
38
+ const actions = useActions(target, scopeKey);
39
+ const [active, setActive] = useState(null);
40
+ const [phase, setPhase] = useState(null);
41
+ const [values, setValues] = useState({});
42
+ const [errors, setErrors] = useState({});
43
+ const [answer, setAnswer] = useState(null);
44
+ const fields = useMemo(() => formFields(active == null ? void 0 : active.input), [active]);
45
+ const working = phase === "working";
46
+ const close = useCallback(() => {
47
+ setPhase(null);
48
+ setActive(null);
49
+ setAnswer(null);
50
+ setErrors({});
51
+ }, []);
52
+ const run = useCallback(async (action, input) => {
53
+ setActive(action);
54
+ setPhase("working");
55
+ try {
56
+ const data = await apiFetch(`${AI_API}/run`, {
57
+ method: "POST",
58
+ body: {
59
+ prompt: action.name,
60
+ input,
61
+ scope: JSON.parse(scopeKey),
62
+ context: typeof context === "function" ? context() : context ?? null
63
+ }
64
+ });
65
+ if (action.mode === "choose") {
66
+ setAnswer(data.data);
67
+ setPhase("choose");
68
+ return;
69
+ }
70
+ onApply == null ? void 0 : onApply(data.data);
71
+ close();
72
+ toast.success("Les champs ont \xE9t\xE9 remplis. Relire avant d'enregistrer.");
73
+ } catch (error) {
74
+ toast.error(error.message || "L'IA n'a pas pu r\xE9pondre.");
75
+ if (formFields(action.input).length > 0) setPhase("form");
76
+ else close();
77
+ }
78
+ }, [close, context, onApply, scopeKey, toast]);
79
+ const [studio, setStudio] = useState(null);
80
+ const start = useCallback((action) => {
81
+ if (action.mode === "studio") {
82
+ setStudio({ action, current: (typeof current === "function" ? current() : current) ?? {} });
83
+ return;
84
+ }
85
+ const actionFields = formFields(action.input);
86
+ if (actionFields.length === 0) {
87
+ run(action, null);
88
+ return;
89
+ }
90
+ setValues(initialValues(actionFields));
91
+ setErrors({});
92
+ setAnswer(null);
93
+ setActive(action);
94
+ setPhase("form");
95
+ }, [current, run]);
96
+ const submitForm = useCallback((event) => {
97
+ event == null ? void 0 : event.preventDefault();
98
+ const { input, errors: found } = readValues(fields, values);
99
+ setErrors(found);
100
+ if (Object.keys(found).length === 0) run(active, input);
101
+ }, [active, fields, run, values]);
102
+ const applyPicked = useCallback((picked) => {
103
+ onApply == null ? void 0 : onApply(applyChoices(active.output, answer, picked));
104
+ close();
105
+ toast.success("La proposition est appliqu\xE9e. Relire avant d'enregistrer.");
106
+ }, [active, answer, close, onApply, toast]);
107
+ if (actions.length === 0) return null;
108
+ const trigger = actions.length === 1 ? /* @__PURE__ */ jsx(AiTrigger, { disabled: disabled || working, iconOnly, size, onClick: () => start(actions[0]), children: actions[0].label }) : /* @__PURE__ */ jsx(
109
+ AiTrigger,
110
+ {
111
+ disabled: disabled || working,
112
+ iconOnly,
113
+ size,
114
+ items: actions.map((action) => ({ label: action.label, description: action.description ?? void 0, onClick: () => start(action) })),
115
+ children: menuLabel
116
+ }
117
+ );
118
+ const media = (preview == null ? void 0 : preview.src) ? /* @__PURE__ */ jsx(AiPreview, { src: preview.src, alt: preview.alt ?? "", label: preview.label }) : null;
119
+ const eyebrow = phase === "choose" ? "Propos\xE9 par l'IA" : phase === "working" ? "L'IA r\xE9dige" : active == null ? void 0 : active.label;
120
+ const title = phase === "working" ? active == null ? void 0 : active.label : (active == null ? void 0 : active.heading) ?? (active == null ? void 0 : active.label);
121
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
122
+ trigger,
123
+ studio && /* @__PURE__ */ jsx(
124
+ AiStudio,
125
+ {
126
+ isOpen: true,
127
+ action: studio.action,
128
+ scope: JSON.parse(scopeKey),
129
+ context,
130
+ current: studio.current,
131
+ onApply: (values2) => onApply == null ? void 0 : onApply(values2),
132
+ onClose: () => setStudio(null)
133
+ }
134
+ ),
135
+ /* @__PURE__ */ jsxs(
136
+ Modal,
137
+ {
138
+ variant: "ai",
139
+ isOpen: phase !== null,
140
+ onClose: close,
141
+ closable: !working,
142
+ loading: working,
143
+ eyebrow,
144
+ title: title ?? "",
145
+ media,
146
+ description: phase === "form" ? (active == null ? void 0 : active.description) ?? void 0 : void 0,
147
+ size: phase === "choose" ? "lg" : "md",
148
+ footer: phase === "form" ? /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3", children: [
149
+ /* @__PURE__ */ jsx("span", { className: "text-[12px] text-fg-muted", children: KEEP_NOTE }),
150
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
151
+ /* @__PURE__ */ jsx(Button, { variant: "secondary", onClick: close, children: "Annuler" }),
152
+ /* @__PURE__ */ jsx(Button, { variant: "ai", type: "submit", form: formId, icon: SparklesIcon, children: "G\xE9n\xE9rer" })
153
+ ] })
154
+ ] }) : null,
155
+ children: [
156
+ phase === "form" && /* @__PURE__ */ jsx("form", { id: formId, onSubmit: submitForm, noValidate: true, children: /* @__PURE__ */ jsx(AiInputForm, { fields, values, errors, onChange: setValues }) }),
157
+ working && /* @__PURE__ */ jsx(AiWorking, { steps: stepsOf(active == null ? void 0 : active.output) }),
158
+ phase === "choose" && answer && /* @__PURE__ */ jsx(AiChoices, { schema: active.output, data: answer, onApply: applyPicked, onCancel: close, note: KEEP_NOTE })
159
+ ]
160
+ }
161
+ )
162
+ ] });
163
+ }
164
+ export {
165
+ AiActions as default
166
+ };