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

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.
package/dist/index.js CHANGED
@@ -105,7 +105,7 @@ var init_builtins = __esm(() => {
105
105
  return { valid: true };
106
106
  }
107
107
  const num = typeof value === "number" ? value : parseFloat(String(value));
108
- if (isNaN(num)) {
108
+ if (Number.isNaN(num)) {
109
109
  return { valid: false, error: "Must be a valid number" };
110
110
  }
111
111
  if (control.min !== undefined && num < control.min) {
@@ -124,7 +124,7 @@ var init_builtins = __esm(() => {
124
124
  if (value === null || value === undefined)
125
125
  return "";
126
126
  const num = typeof value === "number" ? value : parseFloat(String(value));
127
- if (isNaN(num))
127
+ if (Number.isNaN(num))
128
128
  return String(value);
129
129
  return num.toLocaleString();
130
130
  },
@@ -218,14 +218,14 @@ var init_builtins = __esm(() => {
218
218
  return { valid: false, error: "Must be in YYYY-MM-DD format" };
219
219
  }
220
220
  const date = new Date(str);
221
- if (isNaN(date.getTime())) {
221
+ if (Number.isNaN(date.getTime())) {
222
222
  return { valid: false, error: "Invalid date" };
223
223
  }
224
224
  return { valid: true };
225
225
  },
226
226
  parse: (value) => {
227
227
  const date = new Date(value);
228
- if (!isNaN(date.getTime())) {
228
+ if (!Number.isNaN(date.getTime())) {
229
229
  return date.toISOString().split("T")[0];
230
230
  }
231
231
  return value.trim();
@@ -234,7 +234,7 @@ var init_builtins = __esm(() => {
234
234
  if (!value)
235
235
  return "";
236
236
  const date = new Date(String(value));
237
- if (isNaN(date.getTime()))
237
+ if (Number.isNaN(date.getTime()))
238
238
  return String(value);
239
239
  return date.toLocaleDateString();
240
240
  },
@@ -243,7 +243,7 @@ var init_builtins = __esm(() => {
243
243
  fileType = {
244
244
  id: "file",
245
245
  builtin: true,
246
- validate: (value, control) => {
246
+ validate: (value, _control) => {
247
247
  if (value === null || value === undefined) {
248
248
  return { valid: true };
249
249
  }
@@ -319,7 +319,6 @@ function validateField(value, control) {
319
319
  return validateSelect(value, control);
320
320
  case "file":
321
321
  return validateFile(value, control);
322
- case "text":
323
322
  default:
324
323
  return validateText(value, control);
325
324
  }
@@ -370,7 +369,7 @@ function validateEmail(value, control) {
370
369
  }
371
370
  function validateNumber(value, control) {
372
371
  const numValue = typeof value === "number" ? value : parseFloat(String(value).replace(/[,$]/g, ""));
373
- if (isNaN(numValue)) {
372
+ if (Number.isNaN(numValue)) {
374
373
  return {
375
374
  valid: false,
376
375
  error: `${control.label || control.key} must be a number`
@@ -414,7 +413,7 @@ function validateDate(value, control) {
414
413
  error: `${control.label || control.key} must be a valid date`
415
414
  };
416
415
  }
417
- if (isNaN(dateValue.getTime())) {
416
+ if (Number.isNaN(dateValue.getTime())) {
418
417
  return {
419
418
  valid: false,
420
419
  error: `${control.label || control.key} must be a valid date`
@@ -515,9 +514,6 @@ function parseValue(value, control) {
515
514
  const timestamp = Date.parse(value);
516
515
  return Number.isFinite(timestamp) ? new Date(timestamp).toISOString() : value;
517
516
  }
518
- case "text":
519
- case "email":
520
- case "select":
521
517
  default:
522
518
  return value;
523
519
  }
@@ -616,20 +612,13 @@ function isLifecycleIntent(intent) {
616
612
  return ["submit", "stash", "restore", "cancel"].includes(intent);
617
613
  }
618
614
  function isUXIntent(intent) {
619
- return [
620
- "undo",
621
- "skip",
622
- "explain",
623
- "example",
624
- "progress",
625
- "autofill"
626
- ].includes(intent);
615
+ return ["undo", "skip", "explain", "example", "progress", "autofill"].includes(intent);
627
616
  }
628
617
  function hasDataToExtract(intent) {
629
618
  return intent === "fill_form" || intent === "other";
630
619
  }
631
620
 
632
- // ../../node_modules/uuid/dist-node/stringify.js
621
+ // node_modules/uuid/dist-node/stringify.js
633
622
  function unsafeStringify(arr, offset = 0) {
634
623
  return (byteToHex[arr[offset + 0]] + byteToHex[arr[offset + 1]] + byteToHex[arr[offset + 2]] + byteToHex[arr[offset + 3]] + "-" + byteToHex[arr[offset + 4]] + byteToHex[arr[offset + 5]] + "-" + byteToHex[arr[offset + 6]] + byteToHex[arr[offset + 7]] + "-" + byteToHex[arr[offset + 8]] + byteToHex[arr[offset + 9]] + "-" + byteToHex[arr[offset + 10]] + byteToHex[arr[offset + 11]] + byteToHex[arr[offset + 12]] + byteToHex[arr[offset + 13]] + byteToHex[arr[offset + 14]] + byteToHex[arr[offset + 15]]).toLowerCase();
635
624
  }
@@ -641,7 +630,7 @@ var init_stringify = __esm(() => {
641
630
  }
642
631
  });
643
632
 
644
- // ../../node_modules/uuid/dist-node/rng.js
633
+ // node_modules/uuid/dist-node/rng.js
645
634
  import { randomFillSync } from "node:crypto";
646
635
  function rng() {
647
636
  if (poolPtr > rnds8Pool.length - 16) {
@@ -656,14 +645,14 @@ var init_rng = __esm(() => {
656
645
  poolPtr = rnds8Pool.length;
657
646
  });
658
647
 
659
- // ../../node_modules/uuid/dist-node/native.js
648
+ // node_modules/uuid/dist-node/native.js
660
649
  import { randomUUID } from "node:crypto";
661
650
  var native_default;
662
651
  var init_native = __esm(() => {
663
652
  native_default = { randomUUID };
664
653
  });
665
654
 
666
- // ../../node_modules/uuid/dist-node/v4.js
655
+ // node_modules/uuid/dist-node/v4.js
667
656
  function _v4(options, buf, offset) {
668
657
  options = options || {};
669
658
  const rnds = options.random ?? options.rng?.() ?? rng();
@@ -698,7 +687,7 @@ var init_v4 = __esm(() => {
698
687
  v4_default = v4;
699
688
  });
700
689
 
701
- // ../../node_modules/uuid/dist-node/index.js
690
+ // node_modules/uuid/dist-node/index.js
702
691
  var init_dist_node = __esm(() => {
703
692
  init_v4();
704
693
  });
@@ -1040,7 +1029,7 @@ function parseExtractionResponse(response) {
1040
1029
  }
1041
1030
  }
1042
1031
  }
1043
- } catch (error) {
1032
+ } catch (_error) {
1044
1033
  const intentMatch = response.match(/<intent>([^<]+)<\/intent>/);
1045
1034
  if (intentMatch) {
1046
1035
  const intentStr = intentMatch[1].toLowerCase().trim();
@@ -1190,9 +1179,8 @@ Respond in XML:
1190
1179
  }
1191
1180
  }
1192
1181
  var init_extraction = __esm(() => {
1193
- init_validation();
1194
- init_validation();
1195
1182
  init_template();
1183
+ init_validation();
1196
1184
  });
1197
1185
 
1198
1186
  // src/service.ts
@@ -1201,8 +1189,8 @@ __export(exports_service, {
1201
1189
  FormService: () => FormService
1202
1190
  });
1203
1191
  import {
1204
- Service,
1205
- logger
1192
+ logger,
1193
+ Service
1206
1194
  } from "@elizaos/core";
1207
1195
  function prettify3(key) {
1208
1196
  return key.replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
@@ -1210,10 +1198,10 @@ function prettify3(key) {
1210
1198
  var FormService;
1211
1199
  var init_service = __esm(() => {
1212
1200
  init_dist_node();
1213
- init_types();
1201
+ init_builtins();
1214
1202
  init_storage();
1203
+ init_types();
1215
1204
  init_validation();
1216
- init_builtins();
1217
1205
  FormService = class FormService extends Service {
1218
1206
  static serviceType = "FORM";
1219
1207
  capabilityDescription = "Manages conversational forms for data collection";
@@ -1254,13 +1242,6 @@ var init_service = __esm(() => {
1254
1242
  listForms() {
1255
1243
  return Array.from(this.forms.values());
1256
1244
  }
1257
- registerType(type, handler) {
1258
- registerTypeHandler(type, handler);
1259
- logger.debug(`[FormService] Registered type handler: ${type}`);
1260
- }
1261
- getTypeHandler(type) {
1262
- return getTypeHandler(type);
1263
- }
1264
1245
  registerControlType(type, options) {
1265
1246
  const existing = this.controlTypes.get(type.id);
1266
1247
  if (existing) {
@@ -1689,7 +1670,7 @@ var init_service = __esm(() => {
1689
1670
  value,
1690
1671
  externalData
1691
1672
  });
1692
- } catch (error) {
1673
+ } catch (_error) {
1693
1674
  logger.debug(`[FormService] No event handler for FORM_FIELD_CONFIRMED`);
1694
1675
  }
1695
1676
  logger.info(`[FormService] Confirmed external field ${field}`);
@@ -1732,7 +1713,7 @@ var init_service = __esm(() => {
1732
1713
  field,
1733
1714
  reason
1734
1715
  });
1735
- } catch (error) {
1716
+ } catch (_error) {
1736
1717
  logger.debug(`[FormService] No event handler for FORM_FIELD_CANCELLED`);
1737
1718
  }
1738
1719
  logger.info(`[FormService] Cancelled external field ${field}: ${reason}`);
@@ -2024,7 +2005,8 @@ var init_service = __esm(() => {
2024
2005
  id: session.id,
2025
2006
  name: workerName,
2026
2007
  roomId: session.roomId,
2027
- entityId: session.entityId
2008
+ entityId: session.entityId,
2009
+ tags: ["form-hook", hookName]
2028
2010
  };
2029
2011
  await worker.execute(this.runtime, {
2030
2012
  session,
@@ -2049,218 +2031,180 @@ var init_service = __esm(() => {
2049
2031
  };
2050
2032
  });
2051
2033
 
2052
- // src/providers/context.ts
2053
- var exports_context = {};
2054
- __export(exports_context, {
2055
- formContextProvider: () => formContextProvider,
2056
- default: () => context_default
2034
+ // src/actions/restore.ts
2035
+ var exports_restore = {};
2036
+ __export(exports_restore, {
2037
+ formRestoreAction: () => formRestoreAction,
2038
+ default: () => restore_default
2057
2039
  });
2058
- import { logger as logger2 } from "@elizaos/core";
2059
- var formContextProvider, context_default;
2060
- var init_context = __esm(() => {
2061
- init_template();
2062
- formContextProvider = {
2063
- name: "FORM_CONTEXT",
2064
- description: "Provides context about active form sessions",
2065
- get: async (runtime, message, _state) => {
2040
+ import {
2041
+ logger as logger2
2042
+ } from "@elizaos/core";
2043
+ var formRestoreAction, restore_default;
2044
+ var init_restore = __esm(() => {
2045
+ formRestoreAction = {
2046
+ name: "FORM_RESTORE",
2047
+ similes: ["RESUME_FORM", "CONTINUE_FORM"],
2048
+ description: "Restore a previously stashed form session",
2049
+ validate: async (runtime, message, state, options) => {
2050
+ const __avTextRaw = typeof message?.content?.text === "string" ? message.content.text : "";
2051
+ const __avText = __avTextRaw.toLowerCase();
2052
+ const __avKeywords = ["form", "restore"];
2053
+ const __avKeywordOk = __avKeywords.length > 0 && __avKeywords.some((kw) => kw.length > 0 && __avText.includes(kw));
2054
+ const __avRegex = /\b(?:form|restore)\b/i;
2055
+ const __avRegexOk = Boolean(__avText.match(__avRegex));
2056
+ const __avSource = String(message?.content?.source ?? message?.source ?? "");
2057
+ const __avExpectedSource = "";
2058
+ const __avSourceOk = __avExpectedSource ? __avSource === __avExpectedSource : Boolean(__avSource || state || runtime?.agentId || runtime?.getService);
2059
+ const __avOptions = options && typeof options === "object" ? options : {};
2060
+ const __avInputOk = __avText.trim().length > 0 || Object.keys(__avOptions).length > 0 || Boolean(message?.content && typeof message.content === "object");
2061
+ if (!(__avKeywordOk && __avRegexOk && __avSourceOk && __avInputOk)) {
2062
+ return false;
2063
+ }
2064
+ const __avLegacyValidate = async (runtime2, message2, _state) => {
2065
+ try {
2066
+ const text = message2.content?.text || "";
2067
+ const intent = quickIntentDetect(text);
2068
+ if (intent !== "restore") {
2069
+ return false;
2070
+ }
2071
+ const formService = runtime2.getService("FORM");
2072
+ if (!formService) {
2073
+ return false;
2074
+ }
2075
+ const entityId = message2.entityId;
2076
+ if (!entityId)
2077
+ return false;
2078
+ const stashed = await formService.getStashedSessions(entityId);
2079
+ return stashed.length > 0;
2080
+ } catch (error) {
2081
+ logger2.error("[FormRestoreAction] Validation error:", String(error));
2082
+ return false;
2083
+ }
2084
+ };
2085
+ try {
2086
+ return Boolean(await __avLegacyValidate(runtime, message, state, options));
2087
+ } catch {
2088
+ return false;
2089
+ }
2090
+ },
2091
+ handler: async (runtime, message, _state, _options, callback) => {
2066
2092
  try {
2067
2093
  const formService = runtime.getService("FORM");
2068
2094
  if (!formService) {
2069
- return {
2070
- data: { hasActiveForm: false },
2071
- values: { formContext: "" },
2072
- text: ""
2073
- };
2095
+ await callback?.({
2096
+ text: "Sorry, I couldn't find the form service."
2097
+ });
2098
+ return { success: false };
2074
2099
  }
2075
2100
  const entityId = message.entityId;
2076
2101
  const roomId = message.roomId;
2077
2102
  if (!entityId || !roomId) {
2078
- return {
2079
- data: { hasActiveForm: false },
2080
- values: { formContext: "" },
2081
- text: ""
2082
- };
2103
+ await callback?.({
2104
+ text: "Sorry, I couldn't identify you."
2105
+ });
2106
+ return { success: false };
2107
+ }
2108
+ const existing = await formService.getActiveSession(entityId, roomId);
2109
+ if (existing) {
2110
+ const form2 = formService.getForm(existing.formId);
2111
+ await callback?.({
2112
+ text: `You already have an active form: "${form2?.name || existing.formId}". Would you like to continue with that one, or should I save it and restore your other form?`
2113
+ });
2114
+ return { success: false };
2083
2115
  }
2084
- const session = await formService.getActiveSession(entityId, roomId);
2085
2116
  const stashed = await formService.getStashedSessions(entityId);
2086
- if (!session && stashed.length === 0) {
2087
- return {
2088
- data: { hasActiveForm: false, stashedCount: 0 },
2089
- values: { formContext: "" },
2090
- text: ""
2091
- };
2117
+ if (stashed.length === 0) {
2118
+ await callback?.({
2119
+ text: "You don't have any saved forms to resume."
2120
+ });
2121
+ return { success: false };
2092
2122
  }
2093
- let contextText = "";
2094
- let contextState;
2095
- if (session) {
2096
- contextState = formService.getSessionContext(session);
2097
- const form = formService.getForm(session.formId);
2098
- const templateValues = buildTemplateValues(session);
2099
- const resolveText = (value) => renderTemplate(value, templateValues);
2100
- contextState = {
2101
- ...contextState,
2102
- filledFields: contextState.filledFields.map((field) => ({
2103
- ...field,
2104
- label: resolveText(field.label) ?? field.label
2105
- })),
2106
- missingRequired: contextState.missingRequired.map((field) => ({
2107
- ...field,
2108
- label: resolveText(field.label) ?? field.label,
2109
- description: resolveText(field.description),
2110
- askPrompt: resolveText(field.askPrompt)
2111
- })),
2112
- uncertainFields: contextState.uncertainFields.map((field) => ({
2113
- ...field,
2114
- label: resolveText(field.label) ?? field.label
2115
- })),
2116
- nextField: contextState.nextField ? resolveControlTemplates(contextState.nextField, templateValues) : null
2117
- };
2118
- contextText = `# Active Form: ${form?.name || session.formId}
2119
-
2120
- `;
2121
- contextText += `Progress: ${contextState.progress}%
2123
+ const sessionToRestore = stashed.sort((a, b) => b.updatedAt - a.updatedAt)[0];
2124
+ const session = await formService.restore(sessionToRestore.id, entityId);
2125
+ const form = formService.getForm(session.formId);
2126
+ const context = formService.getSessionContext(session);
2127
+ let responseText = `I've restored your "${form?.name || session.formId}" form. `;
2128
+ responseText += `You're ${context.progress}% complete. `;
2129
+ if (context.filledFields.length > 0) {
2130
+ responseText += `
2122
2131
 
2132
+ Here's what I have so far:
2123
2133
  `;
2124
- if (contextState.filledFields.length > 0) {
2125
- contextText += `## Collected Information
2126
- `;
2127
- for (const field of contextState.filledFields) {
2128
- contextText += `- ${field.label}: ${field.displayValue}
2129
- `;
2130
- }
2131
- contextText += `
2134
+ for (const field of context.filledFields) {
2135
+ responseText += `• ${field.label}: ${field.displayValue}
2132
2136
  `;
2133
2137
  }
2134
- if (contextState.missingRequired.length > 0) {
2135
- contextText += `## Still Needed
2136
- `;
2137
- for (const field of contextState.missingRequired) {
2138
- contextText += `- ${field.label}${field.description ? ` (${field.description})` : ""}
2139
- `;
2140
- }
2141
- contextText += `
2142
- `;
2138
+ }
2139
+ if (context.nextField) {
2140
+ responseText += `
2141
+ Let's continue with ${context.nextField.label}.`;
2142
+ if (context.nextField.askPrompt) {
2143
+ responseText += ` ${context.nextField.askPrompt}`;
2143
2144
  }
2144
- if (contextState.uncertainFields.length > 0) {
2145
- contextText += `## Needs Confirmation
2146
- `;
2147
- for (const field of contextState.uncertainFields) {
2148
- contextText += `- ${field.label}: "${field.value}" (${Math.round(field.confidence * 100)}% confident)
2149
- `;
2150
- }
2151
- contextText += `
2152
- `;
2145
+ } else if (context.status === "ready") {
2146
+ responseText += `
2147
+ Everything looks complete! Ready to submit?`;
2148
+ }
2149
+ await callback?.({
2150
+ text: responseText
2151
+ });
2152
+ return {
2153
+ success: true,
2154
+ data: {
2155
+ sessionId: session.id,
2156
+ formId: session.formId,
2157
+ progress: context.progress
2153
2158
  }
2154
- if (contextState.pendingExternalFields.length > 0) {
2155
- contextText += `## Waiting For External Action
2156
- `;
2157
- for (const field of contextState.pendingExternalFields) {
2158
- const ageMs = Date.now() - field.activatedAt;
2159
- const ageMin = Math.floor(ageMs / 60000);
2160
- const ageText = ageMin < 1 ? "just now" : `${ageMin}m ago`;
2161
- contextText += `- ${field.label}: ${field.instructions} (started ${ageText})
2162
- `;
2163
- if (field.address) {
2164
- contextText += ` Address: ${field.address}
2165
- `;
2166
- }
2167
- }
2168
- contextText += `
2169
- `;
2159
+ };
2160
+ } catch (error) {
2161
+ logger2.error("[FormRestoreAction] Handler error:", String(error));
2162
+ await callback?.({
2163
+ text: "Sorry, I couldn't restore your form. Please try again."
2164
+ });
2165
+ return { success: false };
2166
+ }
2167
+ },
2168
+ examples: [
2169
+ [
2170
+ {
2171
+ name: "{{user1}}",
2172
+ content: { text: "Resume my form" }
2173
+ },
2174
+ {
2175
+ name: "{{agentName}}",
2176
+ content: {
2177
+ text: "I've restored your form. Let's continue where you left off."
2170
2178
  }
2171
- contextText += `## Agent Guidance
2172
- `;
2173
- if (contextState.pendingExternalFields.length > 0) {
2174
- const pending = contextState.pendingExternalFields[0];
2175
- contextText += `Waiting for external action. Remind user: "${pending.instructions}"
2176
- `;
2177
- } else if (contextState.pendingCancelConfirmation) {
2178
- contextText += `User is trying to cancel. Confirm: "You've spent time on this. Are you sure you want to cancel?"
2179
- `;
2180
- } else if (contextState.uncertainFields.length > 0) {
2181
- const uncertain = contextState.uncertainFields[0];
2182
- contextText += `Ask user to confirm: "I understood your ${uncertain.label} as '${uncertain.value}'. Is that correct?"
2183
- `;
2184
- } else if (contextState.nextField) {
2185
- const next = contextState.nextField;
2186
- const prompt = next.askPrompt || `Ask for their ${next.label}`;
2187
- contextText += `Next: ${prompt}
2188
- `;
2189
- if (next.example) {
2190
- contextText += `Example: "${next.example}"
2191
- `;
2192
- }
2193
- } else if (contextState.status === "ready") {
2194
- contextText += `All fields collected! Nudge user to submit: "I have everything I need. Ready to submit?"
2195
- `;
2179
+ }
2180
+ ],
2181
+ [
2182
+ {
2183
+ name: "{{user1}}",
2184
+ content: { text: "Continue with my registration" }
2185
+ },
2186
+ {
2187
+ name: "{{agentName}}",
2188
+ content: {
2189
+ text: "I've restored your Registration form. You're 60% complete."
2196
2190
  }
2197
- contextText += `
2198
- `;
2199
- contextText += `## User Can Say
2200
- `;
2201
- contextText += `- Provide information for any field
2202
- `;
2203
- contextText += `- "undo" or "go back" to revert last change
2204
- `;
2205
- contextText += `- "skip" to skip optional fields
2206
- `;
2207
- contextText += `- "why?" to get explanation about a field
2208
- `;
2209
- contextText += `- "how far?" to check progress
2210
- `;
2211
- contextText += `- "submit" or "done" when ready
2212
- `;
2213
- contextText += `- "save for later" to stash the form
2214
- `;
2215
- contextText += `- "cancel" to abandon the form
2216
- `;
2217
- } else {
2218
- contextState = {
2219
- hasActiveForm: false,
2220
- progress: 0,
2221
- filledFields: [],
2222
- missingRequired: [],
2223
- uncertainFields: [],
2224
- nextField: null,
2225
- stashedCount: stashed.length,
2226
- pendingExternalFields: []
2227
- };
2228
2191
  }
2229
- if (stashed.length > 0) {
2230
- contextText += `
2231
- ## Saved Forms
2232
- `;
2233
- contextText += `User has ${stashed.length} saved form(s). They can say "resume" or "continue" to restore one.
2234
- `;
2235
- for (const s of stashed) {
2236
- const form = formService.getForm(s.formId);
2237
- const ctx = formService.getSessionContext(s);
2238
- contextText += `- ${form?.name || s.formId} (${ctx.progress}% complete)
2239
- `;
2192
+ ],
2193
+ [
2194
+ {
2195
+ name: "{{user1}}",
2196
+ content: { text: "Pick up where I left off" }
2197
+ },
2198
+ {
2199
+ name: "{{agentName}}",
2200
+ content: {
2201
+ text: "I've restored your form. Here's what you have so far..."
2240
2202
  }
2241
2203
  }
2242
- return {
2243
- data: JSON.parse(JSON.stringify(contextState)),
2244
- values: {
2245
- formContext: contextText,
2246
- hasActiveForm: String(contextState.hasActiveForm),
2247
- formProgress: String(contextState.progress),
2248
- formStatus: contextState.status || "",
2249
- stashedCount: String(stashed.length)
2250
- },
2251
- text: contextText
2252
- };
2253
- } catch (error) {
2254
- logger2.error("[FormContextProvider] Error:", String(error));
2255
- return {
2256
- data: { hasActiveForm: false, error: true },
2257
- values: { formContext: "Error loading form context." },
2258
- text: "Error loading form context."
2259
- };
2260
- }
2261
- }
2204
+ ]
2205
+ ]
2262
2206
  };
2263
- context_default = formContextProvider;
2207
+ restore_default = formRestoreAction;
2264
2208
  });
2265
2209
 
2266
2210
  // src/evaluators/extractor.ts
@@ -2397,7 +2341,7 @@ var init_extractor = __esm(() => {
2397
2341
  return false;
2398
2342
  }
2399
2343
  },
2400
- handler: async (runtime, message, state) => {
2344
+ handler: async (runtime, message, _state) => {
2401
2345
  try {
2402
2346
  const formService = runtime.getService("FORM");
2403
2347
  if (!formService)
@@ -2460,7 +2404,6 @@ var init_extractor = __esm(() => {
2460
2404
  case "restore":
2461
2405
  logger3.debug("[FormEvaluator] Restore intent - deferring to action");
2462
2406
  break;
2463
- case "fill_form":
2464
2407
  default:
2465
2408
  await processExtractions(runtime, formService, session, form, entityId, extractions, message.id);
2466
2409
  break;
@@ -2480,165 +2423,224 @@ var init_extractor = __esm(() => {
2480
2423
  extractor_default = formEvaluator;
2481
2424
  });
2482
2425
 
2483
- // src/actions/restore.ts
2484
- var exports_restore = {};
2485
- __export(exports_restore, {
2486
- formRestoreAction: () => formRestoreAction,
2487
- default: () => restore_default
2426
+ // src/providers/context.ts
2427
+ var exports_context = {};
2428
+ __export(exports_context, {
2429
+ formContextProvider: () => formContextProvider,
2430
+ default: () => context_default
2488
2431
  });
2489
- import {
2490
- logger as logger4
2491
- } from "@elizaos/core";
2492
- var formRestoreAction, restore_default;
2493
- var init_restore = __esm(() => {
2494
- formRestoreAction = {
2495
- name: "FORM_RESTORE",
2496
- similes: ["RESUME_FORM", "CONTINUE_FORM"],
2497
- description: "Restore a previously stashed form session",
2498
- validate: async (runtime, message, _state) => {
2499
- try {
2500
- const text = message.content?.text || "";
2501
- const intent = quickIntentDetect(text);
2502
- if (intent !== "restore") {
2503
- return false;
2504
- }
2505
- const formService = runtime.getService("FORM");
2506
- if (!formService) {
2507
- return false;
2508
- }
2509
- const entityId = message.entityId;
2510
- if (!entityId)
2511
- return false;
2512
- const stashed = await formService.getStashedSessions(entityId);
2513
- return stashed.length > 0;
2514
- } catch (error) {
2515
- logger4.error("[FormRestoreAction] Validation error:", String(error));
2516
- return false;
2517
- }
2518
- },
2519
- handler: async (runtime, message, _state, _options, callback) => {
2432
+ import { logger as logger4 } from "@elizaos/core";
2433
+ var formContextProvider, context_default;
2434
+ var init_context = __esm(() => {
2435
+ init_template();
2436
+ formContextProvider = {
2437
+ name: "FORM_CONTEXT",
2438
+ description: "Provides context about active form sessions",
2439
+ dynamic: true,
2440
+ get: async (runtime, message, _state) => {
2520
2441
  try {
2521
2442
  const formService = runtime.getService("FORM");
2522
2443
  if (!formService) {
2523
- await callback?.({
2524
- text: "Sorry, I couldn't find the form service."
2525
- });
2526
- return { success: false };
2444
+ return {
2445
+ data: { hasActiveForm: false },
2446
+ values: { formContext: "" },
2447
+ text: ""
2448
+ };
2527
2449
  }
2528
2450
  const entityId = message.entityId;
2529
2451
  const roomId = message.roomId;
2530
2452
  if (!entityId || !roomId) {
2531
- await callback?.({
2532
- text: "Sorry, I couldn't identify you."
2533
- });
2534
- return { success: false };
2535
- }
2536
- const existing = await formService.getActiveSession(entityId, roomId);
2537
- if (existing) {
2538
- const form2 = formService.getForm(existing.formId);
2539
- await callback?.({
2540
- text: `You already have an active form: "${form2?.name || existing.formId}". Would you like to continue with that one, or should I save it and restore your other form?`
2541
- });
2542
- return { success: false };
2453
+ return {
2454
+ data: { hasActiveForm: false },
2455
+ values: { formContext: "" },
2456
+ text: ""
2457
+ };
2543
2458
  }
2459
+ const session = await formService.getActiveSession(entityId, roomId);
2544
2460
  const stashed = await formService.getStashedSessions(entityId);
2545
- if (stashed.length === 0) {
2546
- await callback?.({
2547
- text: "You don't have any saved forms to resume."
2548
- });
2549
- return { success: false };
2461
+ if (!session && stashed.length === 0) {
2462
+ return {
2463
+ data: { hasActiveForm: false, stashedCount: 0 },
2464
+ values: { formContext: "" },
2465
+ text: ""
2466
+ };
2550
2467
  }
2551
- const sessionToRestore = stashed.sort((a, b) => b.updatedAt - a.updatedAt)[0];
2552
- const session = await formService.restore(sessionToRestore.id, entityId);
2553
- const form = formService.getForm(session.formId);
2554
- const context = formService.getSessionContext(session);
2555
- let responseText = `I've restored your "${form?.name || session.formId}" form. `;
2556
- responseText += `You're ${context.progress}% complete. `;
2557
- if (context.filledFields.length > 0) {
2558
- responseText += `
2468
+ let contextText = "";
2469
+ let contextState;
2470
+ if (session) {
2471
+ contextState = formService.getSessionContext(session);
2472
+ const form = formService.getForm(session.formId);
2473
+ const templateValues = buildTemplateValues(session);
2474
+ const resolveText = (value) => renderTemplate(value, templateValues);
2475
+ contextState = {
2476
+ ...contextState,
2477
+ filledFields: contextState.filledFields.map((field) => ({
2478
+ ...field,
2479
+ label: resolveText(field.label) ?? field.label
2480
+ })),
2481
+ missingRequired: contextState.missingRequired.map((field) => ({
2482
+ ...field,
2483
+ label: resolveText(field.label) ?? field.label,
2484
+ description: resolveText(field.description),
2485
+ askPrompt: resolveText(field.askPrompt)
2486
+ })),
2487
+ uncertainFields: contextState.uncertainFields.map((field) => ({
2488
+ ...field,
2489
+ label: resolveText(field.label) ?? field.label
2490
+ })),
2491
+ nextField: contextState.nextField ? resolveControlTemplates(contextState.nextField, templateValues) : null
2492
+ };
2493
+ contextText = `# Active Form: ${form?.name || session.formId}
2559
2494
 
2560
- Here's what I have so far:
2561
2495
  `;
2562
- for (const field of context.filledFields) {
2563
- responseText += `• ${field.label}: ${field.displayValue}
2496
+ contextText += `Progress: ${contextState.progress}%
2497
+
2498
+ `;
2499
+ if (contextState.filledFields.length > 0) {
2500
+ contextText += `## Collected Information
2501
+ `;
2502
+ for (const field of contextState.filledFields) {
2503
+ contextText += `- ${field.label}: ${field.displayValue}
2504
+ `;
2505
+ }
2506
+ contextText += `
2564
2507
  `;
2565
2508
  }
2566
- }
2567
- if (context.nextField) {
2568
- responseText += `
2569
- Let's continue with ${context.nextField.label}.`;
2570
- if (context.nextField.askPrompt) {
2571
- responseText += ` ${context.nextField.askPrompt}`;
2509
+ if (contextState.missingRequired.length > 0) {
2510
+ contextText += `## Still Needed
2511
+ `;
2512
+ for (const field of contextState.missingRequired) {
2513
+ contextText += `- ${field.label}${field.description ? ` (${field.description})` : ""}
2514
+ `;
2515
+ }
2516
+ contextText += `
2517
+ `;
2572
2518
  }
2573
- } else if (context.status === "ready") {
2574
- responseText += `
2575
- Everything looks complete! Ready to submit?`;
2576
- }
2577
- await callback?.({
2578
- text: responseText
2579
- });
2580
- return {
2581
- success: true,
2582
- data: {
2583
- sessionId: session.id,
2584
- formId: session.formId,
2585
- progress: context.progress
2519
+ if (contextState.uncertainFields.length > 0) {
2520
+ contextText += `## Needs Confirmation
2521
+ `;
2522
+ for (const field of contextState.uncertainFields) {
2523
+ contextText += `- ${field.label}: "${field.value}" (${Math.round(field.confidence * 100)}% confident)
2524
+ `;
2525
+ }
2526
+ contextText += `
2527
+ `;
2586
2528
  }
2587
- };
2588
- } catch (error) {
2589
- logger4.error("[FormRestoreAction] Handler error:", String(error));
2590
- await callback?.({
2591
- text: "Sorry, I couldn't restore your form. Please try again."
2592
- });
2593
- return { success: false };
2594
- }
2595
- },
2596
- examples: [
2597
- [
2598
- {
2599
- name: "{{user1}}",
2600
- content: { text: "Resume my form" }
2601
- },
2602
- {
2603
- name: "{{agentName}}",
2604
- content: {
2605
- text: "I've restored your form. Let's continue where you left off."
2529
+ if (contextState.pendingExternalFields.length > 0) {
2530
+ contextText += `## Waiting For External Action
2531
+ `;
2532
+ for (const field of contextState.pendingExternalFields) {
2533
+ const ageMs = Date.now() - field.activatedAt;
2534
+ const ageMin = Math.floor(ageMs / 60000);
2535
+ const ageText = ageMin < 1 ? "just now" : `${ageMin}m ago`;
2536
+ contextText += `- ${field.label}: ${field.instructions} (started ${ageText})
2537
+ `;
2538
+ if (field.address) {
2539
+ contextText += ` Address: ${field.address}
2540
+ `;
2541
+ }
2542
+ }
2543
+ contextText += `
2544
+ `;
2606
2545
  }
2607
- }
2608
- ],
2609
- [
2610
- {
2611
- name: "{{user1}}",
2612
- content: { text: "Continue with my registration" }
2613
- },
2614
- {
2615
- name: "{{agentName}}",
2616
- content: {
2617
- text: "I've restored your Registration form. You're 60% complete."
2546
+ contextText += `## Agent Guidance
2547
+ `;
2548
+ if (contextState.pendingExternalFields.length > 0) {
2549
+ const pending = contextState.pendingExternalFields[0];
2550
+ contextText += `Waiting for external action. Remind user: "${pending.instructions}"
2551
+ `;
2552
+ } else if (contextState.pendingCancelConfirmation) {
2553
+ contextText += `User is trying to cancel. Confirm: "You've spent time on this. Are you sure you want to cancel?"
2554
+ `;
2555
+ } else if (contextState.uncertainFields.length > 0) {
2556
+ const uncertain = contextState.uncertainFields[0];
2557
+ contextText += `Ask user to confirm: "I understood your ${uncertain.label} as '${uncertain.value}'. Is that correct?"
2558
+ `;
2559
+ } else if (contextState.nextField) {
2560
+ const next = contextState.nextField;
2561
+ const prompt = next.askPrompt || `Ask for their ${next.label}`;
2562
+ contextText += `Next: ${prompt}
2563
+ `;
2564
+ if (next.example) {
2565
+ contextText += `Example: "${next.example}"
2566
+ `;
2567
+ }
2568
+ } else if (contextState.status === "ready") {
2569
+ contextText += `All fields collected! Nudge user to submit: "I have everything I need. Ready to submit?"
2570
+ `;
2618
2571
  }
2572
+ contextText += `
2573
+ `;
2574
+ contextText += `## User Can Say
2575
+ `;
2576
+ contextText += `- Provide information for any field
2577
+ `;
2578
+ contextText += `- "undo" or "go back" to revert last change
2579
+ `;
2580
+ contextText += `- "skip" to skip optional fields
2581
+ `;
2582
+ contextText += `- "why?" to get explanation about a field
2583
+ `;
2584
+ contextText += `- "how far?" to check progress
2585
+ `;
2586
+ contextText += `- "submit" or "done" when ready
2587
+ `;
2588
+ contextText += `- "save for later" to stash the form
2589
+ `;
2590
+ contextText += `- "cancel" to abandon the form
2591
+ `;
2592
+ } else {
2593
+ contextState = {
2594
+ hasActiveForm: false,
2595
+ progress: 0,
2596
+ filledFields: [],
2597
+ missingRequired: [],
2598
+ uncertainFields: [],
2599
+ nextField: null,
2600
+ stashedCount: stashed.length,
2601
+ pendingExternalFields: []
2602
+ };
2619
2603
  }
2620
- ],
2621
- [
2622
- {
2623
- name: "{{user1}}",
2624
- content: { text: "Pick up where I left off" }
2625
- },
2626
- {
2627
- name: "{{agentName}}",
2628
- content: {
2629
- text: "I've restored your form. Here's what you have so far..."
2604
+ if (stashed.length > 0) {
2605
+ contextText += `
2606
+ ## Saved Forms
2607
+ `;
2608
+ contextText += `User has ${stashed.length} saved form(s). They can say "resume" or "continue" to restore one.
2609
+ `;
2610
+ for (const s of stashed) {
2611
+ const form = formService.getForm(s.formId);
2612
+ const ctx = formService.getSessionContext(s);
2613
+ contextText += `- ${form?.name || s.formId} (${ctx.progress}% complete)
2614
+ `;
2630
2615
  }
2631
2616
  }
2632
- ]
2633
- ]
2617
+ return {
2618
+ data: JSON.parse(JSON.stringify(contextState)),
2619
+ values: {
2620
+ formContext: contextText,
2621
+ hasActiveForm: String(contextState.hasActiveForm),
2622
+ formProgress: String(contextState.progress),
2623
+ formStatus: contextState.status || "",
2624
+ stashedCount: String(stashed.length)
2625
+ },
2626
+ text: contextText
2627
+ };
2628
+ } catch (error) {
2629
+ logger4.error("[FormContextProvider] Error:", String(error));
2630
+ return {
2631
+ data: { hasActiveForm: false, error: true },
2632
+ values: { formContext: "Error loading form context." },
2633
+ text: "Error loading form context."
2634
+ };
2635
+ }
2636
+ }
2634
2637
  };
2635
- restore_default = formRestoreAction;
2638
+ context_default = formContextProvider;
2636
2639
  });
2637
2640
 
2638
- // src/index.ts
2641
+ // index.ts
2639
2642
  init_builtins();
2640
2643
  init_validation();
2641
- init_validation();
2642
2644
  init_storage();
2643
2645
  init_extraction();
2644
2646
  init_types();
@@ -2963,7 +2965,7 @@ class FormBuilder {
2963
2965
  }
2964
2966
  control(builder) {
2965
2967
  const ctrl = builder instanceof ControlBuilder ? builder.build() : builder;
2966
- this.form.controls.push(ctrl);
2968
+ this.form.controls?.push(ctrl);
2967
2969
  return this;
2968
2970
  }
2969
2971
  controls(...builders) {
@@ -3080,11 +3082,11 @@ function prettify2(key) {
3080
3082
  return key.replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
3081
3083
  }
3082
3084
 
3083
- // src/index.ts
3085
+ // index.ts
3084
3086
  init_service();
3085
- init_context();
3086
- init_extractor();
3087
3087
  init_restore();
3088
+ init_extractor();
3089
+ init_context();
3088
3090
 
3089
3091
  // src/tasks/nudge.ts
3090
3092
  import { logger as logger5 } from "@elizaos/core";
@@ -3175,7 +3177,7 @@ async function sendExpirationWarning(runtime, session, form) {
3175
3177
  }
3176
3178
  }
3177
3179
 
3178
- // src/index.ts
3180
+ // index.ts
3179
3181
  var formPlugin = {
3180
3182
  name: "form",
3181
3183
  description: "Agent-native conversational forms for data collection",
@@ -3192,6 +3194,7 @@ var formPlugin = {
3192
3194
  {
3193
3195
  name: "FORM_CONTEXT",
3194
3196
  description: "Provides context about active form sessions",
3197
+ dynamic: true,
3195
3198
  get: async (runtime, message, state) => {
3196
3199
  const { formContextProvider: formContextProvider2 } = await Promise.resolve().then(() => (init_context(), exports_context));
3197
3200
  return formContextProvider2.get(runtime, message, state);
@@ -3244,7 +3247,7 @@ var formPlugin = {
3244
3247
  }
3245
3248
  ]
3246
3249
  };
3247
- var src_default = formPlugin;
3250
+ var typescript_default = formPlugin;
3248
3251
  export {
3249
3252
  validateField,
3250
3253
  shouldNudge,
@@ -3284,7 +3287,7 @@ export {
3284
3287
  extractSingleField,
3285
3288
  detectCorrection,
3286
3289
  deleteSession,
3287
- src_default as default,
3290
+ typescript_default as default,
3288
3291
  clearTypeHandlers,
3289
3292
  calculateTTL,
3290
3293
  applyFormDefaults,
@@ -3303,4 +3306,4 @@ export {
3303
3306
  BUILTIN_TYPES
3304
3307
  };
3305
3308
 
3306
- //# debugId=11830BB10982F7F464756E2164756E21
3309
+ //# debugId=DC90FD3CDE2C7E4764756E2164756E21