@elizaos/plugin-form 2.0.0-alpha.1 → 2.0.0-alpha.11

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 (48) hide show
  1. package/dist/chunk-4B5QLNVA.js +187 -0
  2. package/dist/chunk-ARWZY3NX.js +284 -0
  3. package/dist/chunk-R4VBS2YK.js +1597 -0
  4. package/dist/chunk-TBCL2ILB.js +172 -0
  5. package/dist/chunk-WY4WK3HD.js +57 -0
  6. package/dist/chunk-XHECCAUT.js +544 -0
  7. package/dist/chunk-YTWANJ3R.js +64 -0
  8. package/dist/context-MHPFYZZ2.js +9 -0
  9. package/dist/extractor-UWASKXKD.js +11 -0
  10. package/dist/index.d.ts +3057 -19
  11. package/dist/index.js +428 -2826
  12. package/dist/restore-S7JLME4H.js +9 -0
  13. package/dist/service-TCCXKV3T.js +7 -0
  14. package/package.json +33 -23
  15. package/LICENSE +0 -21
  16. package/README.md +0 -846
  17. package/dist/actions/restore.d.ts +0 -62
  18. package/dist/actions/restore.d.ts.map +0 -1
  19. package/dist/builder.d.ts +0 -320
  20. package/dist/builder.d.ts.map +0 -1
  21. package/dist/builtins.d.ts +0 -128
  22. package/dist/builtins.d.ts.map +0 -1
  23. package/dist/defaults.d.ts +0 -95
  24. package/dist/defaults.d.ts.map +0 -1
  25. package/dist/evaluators/extractor.d.ts +0 -91
  26. package/dist/evaluators/extractor.d.ts.map +0 -1
  27. package/dist/extraction.d.ts +0 -105
  28. package/dist/extraction.d.ts.map +0 -1
  29. package/dist/index.d.ts.map +0 -1
  30. package/dist/index.js.map +0 -30
  31. package/dist/intent.d.ts +0 -116
  32. package/dist/intent.d.ts.map +0 -1
  33. package/dist/providers/context.d.ts +0 -69
  34. package/dist/providers/context.d.ts.map +0 -1
  35. package/dist/service.d.ts +0 -417
  36. package/dist/service.d.ts.map +0 -1
  37. package/dist/storage.d.ts +0 -228
  38. package/dist/storage.d.ts.map +0 -1
  39. package/dist/tasks/nudge.d.ts +0 -89
  40. package/dist/tasks/nudge.d.ts.map +0 -1
  41. package/dist/template.d.ts +0 -10
  42. package/dist/template.d.ts.map +0 -1
  43. package/dist/ttl.d.ts +0 -144
  44. package/dist/ttl.d.ts.map +0 -1
  45. package/dist/types.d.ts +0 -1214
  46. package/dist/types.d.ts.map +0 -1
  47. package/dist/validation.d.ts +0 -156
  48. package/dist/validation.d.ts.map +0 -1
@@ -0,0 +1,187 @@
1
+ import {
2
+ buildTemplateValues,
3
+ renderTemplate,
4
+ resolveControlTemplates
5
+ } from "./chunk-WY4WK3HD.js";
6
+
7
+ // src/providers/context.ts
8
+ import { logger } from "@elizaos/core";
9
+ var formContextProvider = {
10
+ name: "FORM_CONTEXT",
11
+ description: "Provides context about active form sessions",
12
+ /**
13
+ * Get form context for the current message.
14
+ *
15
+ * @param runtime - Agent runtime for service access
16
+ * @param message - The user message being processed
17
+ * @param _state - Current agent state (unused)
18
+ * @returns Provider result with form context (data, values, text)
19
+ */
20
+ get: async (runtime, message, _state) => {
21
+ try {
22
+ const formService = runtime.getService("FORM");
23
+ if (!formService) {
24
+ return { data: { hasActiveForm: false }, values: { formContext: "" }, text: "" };
25
+ }
26
+ const entityId = message.entityId;
27
+ const roomId = message.roomId;
28
+ if (!entityId || !roomId) {
29
+ return { data: { hasActiveForm: false }, values: { formContext: "" }, text: "" };
30
+ }
31
+ const session = await formService.getActiveSession(entityId, roomId);
32
+ const stashed = await formService.getStashedSessions(entityId);
33
+ if (!session && stashed.length === 0) {
34
+ return {
35
+ data: { hasActiveForm: false, stashedCount: 0 },
36
+ values: { formContext: "" },
37
+ text: ""
38
+ };
39
+ }
40
+ let contextText = "";
41
+ let contextState;
42
+ if (session) {
43
+ contextState = formService.getSessionContext(session);
44
+ const form = formService.getForm(session.formId);
45
+ const templateValues = buildTemplateValues(session);
46
+ const resolve = (v) => renderTemplate(v, templateValues);
47
+ contextState = {
48
+ ...contextState,
49
+ filledFields: contextState.filledFields.map((f) => ({
50
+ ...f,
51
+ label: resolve(f.label) ?? f.label
52
+ })),
53
+ missingRequired: contextState.missingRequired.map((f) => ({
54
+ ...f,
55
+ label: resolve(f.label) ?? f.label,
56
+ description: resolve(f.description),
57
+ askPrompt: resolve(f.askPrompt)
58
+ })),
59
+ uncertainFields: contextState.uncertainFields.map((f) => ({
60
+ ...f,
61
+ label: resolve(f.label) ?? f.label
62
+ })),
63
+ nextField: contextState.nextField ? resolveControlTemplates(contextState.nextField, templateValues) : null
64
+ };
65
+ const controls = form?.controls ?? [];
66
+ const filledKeys = new Set(contextState.filledFields.map((f) => f.key));
67
+ const controlByKey = new Map(controls.map((c) => [c.key, c]));
68
+ const requiredFilled = contextState.filledFields.filter(
69
+ (f) => controlByKey.get(f.key)?.required
70
+ );
71
+ const optionalFilled = contextState.filledFields.filter(
72
+ (f) => !controlByKey.get(f.key)?.required
73
+ );
74
+ const optionalMissing = controls.filter(
75
+ (c) => !c.hidden && !c.required && !filledKeys.has(c.key)
76
+ );
77
+ const fmt = (items) => items.length === 0 ? "none" : items.map((i) => i.displayValue ? `${i.key} (${i.displayValue})` : i.key).join(", ");
78
+ contextText = `# Active Form: ${form?.name || session.formId}
79
+ `;
80
+ contextText += `Progress: ${contextState.progress}%
81
+
82
+ `;
83
+ contextText += `Required fields we don't have: ${fmt(contextState.missingRequired)}
84
+ `;
85
+ contextText += `Required fields we do have: ${fmt(requiredFilled)}
86
+
87
+ `;
88
+ contextText += `Optional fields we don't have: ${fmt(optionalMissing)}
89
+ `;
90
+ contextText += `Optional fields we do have: ${fmt(optionalFilled)}
91
+
92
+ `;
93
+ if (contextState.uncertainFields.length > 0) {
94
+ contextText += `Needs confirmation:
95
+ `;
96
+ for (const f of contextState.uncertainFields) {
97
+ contextText += `- ${f.label}: "${f.value}" (${Math.round(f.confidence * 100)}% confident)
98
+ `;
99
+ }
100
+ contextText += "\n";
101
+ }
102
+ if (contextState.pendingExternalFields.length > 0) {
103
+ contextText += `Waiting for external action:
104
+ `;
105
+ for (const f of contextState.pendingExternalFields) {
106
+ const mins = Math.floor((Date.now() - f.activatedAt) / 6e4);
107
+ contextText += `- ${f.label}: ${f.instructions} (${mins < 1 ? "just now" : `${mins}m ago`})`;
108
+ if (f.address) contextText += ` Address: ${f.address}`;
109
+ contextText += "\n";
110
+ }
111
+ contextText += "\n";
112
+ }
113
+ if (contextState.pendingExternalFields.length > 0) {
114
+ const p = contextState.pendingExternalFields[0];
115
+ contextText += `Instruction: Waiting for external action. Remind user: "${p.instructions}"
116
+ `;
117
+ } else if (contextState.pendingCancelConfirmation) {
118
+ contextText += `Instruction: User is trying to cancel. Confirm they really want to lose progress.
119
+ `;
120
+ } else if (contextState.uncertainFields.length > 0) {
121
+ const u = contextState.uncertainFields[0];
122
+ contextText += `Instruction: Ask user to confirm "${u.label}" = "${u.value}".
123
+ `;
124
+ } else if (contextState.missingRequired.length > 0) {
125
+ contextText += `Instruction: Please nudge the user into helping complete required fields. The user can provide one or several answers in a single message; the form accepts them all.
126
+ `;
127
+ } else if (contextState.status === "ready") {
128
+ contextText += `Instruction: All required fields collected. Nudge user to submit.
129
+ `;
130
+ } else if (optionalMissing.length > 0) {
131
+ contextText += `Instruction: Required fields are done. Optionally nudge for remaining optional fields, or nudge to submit.
132
+ `;
133
+ }
134
+ } else {
135
+ contextState = {
136
+ hasActiveForm: false,
137
+ progress: 0,
138
+ filledFields: [],
139
+ missingRequired: [],
140
+ uncertainFields: [],
141
+ nextField: null,
142
+ stashedCount: stashed.length,
143
+ pendingExternalFields: []
144
+ };
145
+ }
146
+ if (stashed.length > 0) {
147
+ contextText += `
148
+ Saved forms: User has ${stashed.length} saved form(s). They can say "resume" to restore one.
149
+ `;
150
+ for (const s of stashed) {
151
+ const f = formService.getForm(s.formId);
152
+ const ctx = formService.getSessionContext(s);
153
+ contextText += `- ${f?.name || s.formId} (${ctx.progress}% complete)
154
+ `;
155
+ }
156
+ }
157
+ return {
158
+ // Full context object for programmatic access
159
+ // WHY: Restore action and others read data.nextField, data.filledFields, etc.
160
+ data: JSON.parse(JSON.stringify(contextState)),
161
+ // String values for template substitution (e.g. in prompts: formContext, formProgress, formStatus)
162
+ values: {
163
+ formContext: contextText,
164
+ hasActiveForm: String(contextState.hasActiveForm),
165
+ formProgress: String(contextState.progress),
166
+ formStatus: contextState.status || "",
167
+ stashedCount: String(stashed.length)
168
+ },
169
+ // Human-readable text for agent (injected into prompt)
170
+ text: contextText
171
+ };
172
+ } catch (error) {
173
+ logger.error("[FormContextProvider] Error:", String(error));
174
+ return {
175
+ data: { hasActiveForm: false, error: true },
176
+ values: { formContext: "Error loading form context." },
177
+ text: "Error loading form context."
178
+ };
179
+ }
180
+ }
181
+ };
182
+ var context_default = formContextProvider;
183
+
184
+ export {
185
+ formContextProvider,
186
+ context_default
187
+ };
@@ -0,0 +1,284 @@
1
+ // src/validation.ts
2
+ var typeHandlers = /* @__PURE__ */ new Map();
3
+ function registerTypeHandler(type, handler) {
4
+ typeHandlers.set(type, handler);
5
+ }
6
+ function getTypeHandler(type) {
7
+ return typeHandlers.get(type);
8
+ }
9
+ function clearTypeHandlers() {
10
+ typeHandlers.clear();
11
+ }
12
+ function validateField(value, control) {
13
+ if (control.required) {
14
+ if (value === void 0 || value === null || value === "") {
15
+ return {
16
+ valid: false,
17
+ error: `${control.label || control.key} is required`
18
+ };
19
+ }
20
+ }
21
+ if (value === void 0 || value === null || value === "") {
22
+ return { valid: true };
23
+ }
24
+ const handler = typeHandlers.get(control.type);
25
+ if (handler?.validate) {
26
+ const result = handler.validate(value, control);
27
+ if (!result.valid) {
28
+ return result;
29
+ }
30
+ }
31
+ switch (control.type) {
32
+ case "email":
33
+ return validateEmail(value, control);
34
+ case "number":
35
+ return validateNumber(value, control);
36
+ case "boolean":
37
+ return validateBoolean(value, control);
38
+ case "date":
39
+ return validateDate(value, control);
40
+ case "select":
41
+ return validateSelect(value, control);
42
+ case "file":
43
+ return validateFile(value, control);
44
+ default:
45
+ return validateText(value, control);
46
+ }
47
+ }
48
+ function validateText(value, control) {
49
+ const strValue = String(value);
50
+ if (control.pattern) {
51
+ const regex = new RegExp(control.pattern);
52
+ if (!regex.test(strValue)) {
53
+ return {
54
+ valid: false,
55
+ error: `${control.label || control.key} has invalid format`
56
+ };
57
+ }
58
+ }
59
+ if (control.minLength !== void 0 && strValue.length < control.minLength) {
60
+ return {
61
+ valid: false,
62
+ error: `${control.label || control.key} must be at least ${control.minLength} characters`
63
+ };
64
+ }
65
+ if (control.maxLength !== void 0 && strValue.length > control.maxLength) {
66
+ return {
67
+ valid: false,
68
+ error: `${control.label || control.key} must be at most ${control.maxLength} characters`
69
+ };
70
+ }
71
+ if (control.enum && control.enum.length > 0) {
72
+ if (!control.enum.includes(strValue)) {
73
+ return {
74
+ valid: false,
75
+ error: `${control.label || control.key} must be one of: ${control.enum.join(", ")}`
76
+ };
77
+ }
78
+ }
79
+ return { valid: true };
80
+ }
81
+ function validateEmail(value, control) {
82
+ const strValue = String(value);
83
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
84
+ if (!emailRegex.test(strValue)) {
85
+ return {
86
+ valid: false,
87
+ error: `${control.label || control.key} must be a valid email address`
88
+ };
89
+ }
90
+ return validateText(value, control);
91
+ }
92
+ function validateNumber(value, control) {
93
+ const numValue = typeof value === "number" ? value : parseFloat(String(value).replace(/[,$]/g, ""));
94
+ if (Number.isNaN(numValue)) {
95
+ return {
96
+ valid: false,
97
+ error: `${control.label || control.key} must be a number`
98
+ };
99
+ }
100
+ if (control.min !== void 0 && numValue < control.min) {
101
+ return {
102
+ valid: false,
103
+ error: `${control.label || control.key} must be at least ${control.min}`
104
+ };
105
+ }
106
+ if (control.max !== void 0 && numValue > control.max) {
107
+ return {
108
+ valid: false,
109
+ error: `${control.label || control.key} must be at most ${control.max}`
110
+ };
111
+ }
112
+ return { valid: true };
113
+ }
114
+ function validateBoolean(value, _control) {
115
+ if (typeof value === "boolean") {
116
+ return { valid: true };
117
+ }
118
+ const strValue = String(value).toLowerCase();
119
+ const truthy = ["true", "yes", "1", "on"];
120
+ const falsy = ["false", "no", "0", "off"];
121
+ if (truthy.includes(strValue) || falsy.includes(strValue)) {
122
+ return { valid: true };
123
+ }
124
+ return { valid: false, error: "Must be true or false" };
125
+ }
126
+ function validateDate(value, control) {
127
+ let dateValue;
128
+ if (value instanceof Date) {
129
+ dateValue = value;
130
+ } else if (typeof value === "string" || typeof value === "number") {
131
+ dateValue = new Date(value);
132
+ } else {
133
+ return {
134
+ valid: false,
135
+ error: `${control.label || control.key} must be a valid date`
136
+ };
137
+ }
138
+ if (Number.isNaN(dateValue.getTime())) {
139
+ return {
140
+ valid: false,
141
+ error: `${control.label || control.key} must be a valid date`
142
+ };
143
+ }
144
+ if (control.min !== void 0 && dateValue.getTime() < control.min) {
145
+ return {
146
+ valid: false,
147
+ error: `${control.label || control.key} is too early`
148
+ };
149
+ }
150
+ if (control.max !== void 0 && dateValue.getTime() > control.max) {
151
+ return {
152
+ valid: false,
153
+ error: `${control.label || control.key} is too late`
154
+ };
155
+ }
156
+ return { valid: true };
157
+ }
158
+ function validateSelect(value, control) {
159
+ if (!control.options || control.options.length === 0) {
160
+ return { valid: true };
161
+ }
162
+ const strValue = String(value);
163
+ const validValues = control.options.map((opt) => opt.value);
164
+ if (!validValues.includes(strValue)) {
165
+ return {
166
+ valid: false,
167
+ error: `${control.label || control.key} must be one of the available options`
168
+ };
169
+ }
170
+ return { valid: true };
171
+ }
172
+ function validateFile(value, control) {
173
+ if (!control.file) {
174
+ return { valid: true };
175
+ }
176
+ const files = Array.isArray(value) ? value : [value];
177
+ if (control.file.maxFiles && files.length > control.file.maxFiles) {
178
+ return {
179
+ valid: false,
180
+ error: `Maximum ${control.file.maxFiles} files allowed`
181
+ };
182
+ }
183
+ for (const file of files) {
184
+ if (!file || typeof file !== "object") continue;
185
+ const fileObj = file;
186
+ if (control.file.maxSize && fileObj.size && fileObj.size > control.file.maxSize) {
187
+ return {
188
+ valid: false,
189
+ error: `File size exceeds maximum of ${formatBytes(control.file.maxSize)}`
190
+ };
191
+ }
192
+ if (control.file.accept && fileObj.mimeType) {
193
+ const accepted = control.file.accept.some(
194
+ (pattern) => matchesMimeType(fileObj.mimeType, pattern)
195
+ );
196
+ if (!accepted) {
197
+ return {
198
+ valid: false,
199
+ error: `File type ${fileObj.mimeType} is not accepted`
200
+ };
201
+ }
202
+ }
203
+ }
204
+ return { valid: true };
205
+ }
206
+ function matchesMimeType(mimeType, pattern) {
207
+ if (pattern === "*/*") return true;
208
+ if (pattern.endsWith("/*")) {
209
+ const prefix = pattern.slice(0, -1);
210
+ return mimeType.startsWith(prefix);
211
+ }
212
+ return mimeType === pattern;
213
+ }
214
+ function formatBytes(bytes) {
215
+ if (bytes < 1024) return `${bytes} B`;
216
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
217
+ if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
218
+ return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} GB`;
219
+ }
220
+ function parseValue(value, control) {
221
+ const handler = typeHandlers.get(control.type);
222
+ if (handler?.parse) {
223
+ return handler.parse(value);
224
+ }
225
+ switch (control.type) {
226
+ case "number":
227
+ return parseFloat(value.replace(/[,$]/g, ""));
228
+ case "boolean": {
229
+ const lower = value.toLowerCase();
230
+ return ["true", "yes", "1", "on"].includes(lower);
231
+ }
232
+ case "date": {
233
+ const timestamp = Date.parse(value);
234
+ return Number.isFinite(timestamp) ? new Date(timestamp).toISOString() : value;
235
+ }
236
+ default:
237
+ return value;
238
+ }
239
+ }
240
+ function formatValue(value, control) {
241
+ if (value === void 0 || value === null) return "";
242
+ const handler = typeHandlers.get(control.type);
243
+ if (handler?.format) {
244
+ return handler.format(value);
245
+ }
246
+ if (control.sensitive) {
247
+ const strVal = String(value);
248
+ if (strVal.length > 8) {
249
+ return `${strVal.slice(0, 4)}...${strVal.slice(-4)}`;
250
+ }
251
+ return "****";
252
+ }
253
+ switch (control.type) {
254
+ case "number":
255
+ return typeof value === "number" ? value.toLocaleString() : String(value);
256
+ case "boolean":
257
+ return value ? "Yes" : "No";
258
+ case "date":
259
+ return value instanceof Date ? value.toLocaleDateString() : String(value);
260
+ case "select":
261
+ if (control.options) {
262
+ const option = control.options.find((opt) => opt.value === String(value));
263
+ if (option) return option.label;
264
+ }
265
+ return String(value);
266
+ case "file":
267
+ if (Array.isArray(value)) {
268
+ return value.map((f) => f.name || "file").join(", ");
269
+ }
270
+ return value.name || "file";
271
+ default:
272
+ return String(value);
273
+ }
274
+ }
275
+
276
+ export {
277
+ registerTypeHandler,
278
+ getTypeHandler,
279
+ clearTypeHandlers,
280
+ validateField,
281
+ matchesMimeType,
282
+ parseValue,
283
+ formatValue
284
+ };