@elizaos/plugin-form 2.0.0-beta.1 → 2.0.11-beta.7

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 (59) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +134 -0
  3. package/package.json +21 -4
  4. package/dist/actions/restore.d.ts +0 -25
  5. package/dist/actions/restore.d.ts.map +0 -1
  6. package/dist/actions/restore.js +0 -176
  7. package/dist/actions/restore.js.map +0 -1
  8. package/dist/builder.d.ts +0 -320
  9. package/dist/builder.d.ts.map +0 -1
  10. package/dist/builder.js +0 -458
  11. package/dist/builder.js.map +0 -1
  12. package/dist/builtins.d.ts +0 -128
  13. package/dist/builtins.d.ts.map +0 -1
  14. package/dist/builtins.js +0 -233
  15. package/dist/builtins.js.map +0 -1
  16. package/dist/defaults.d.ts +0 -95
  17. package/dist/defaults.d.ts.map +0 -1
  18. package/dist/defaults.js +0 -79
  19. package/dist/defaults.js.map +0 -1
  20. package/dist/evaluators/extractor.d.ts +0 -28
  21. package/dist/evaluators/extractor.d.ts.map +0 -1
  22. package/dist/evaluators/extractor.js +0 -247
  23. package/dist/evaluators/extractor.js.map +0 -1
  24. package/dist/extraction.d.ts +0 -55
  25. package/dist/extraction.d.ts.map +0 -1
  26. package/dist/extraction.js +0 -331
  27. package/dist/extraction.js.map +0 -1
  28. package/dist/index.d.ts +0 -31
  29. package/dist/index.d.ts.map +0 -1
  30. package/dist/index.js +0 -144
  31. package/dist/index.js.map +0 -1
  32. package/dist/providers/context.d.ts +0 -56
  33. package/dist/providers/context.d.ts.map +0 -1
  34. package/dist/providers/context.js +0 -206
  35. package/dist/providers/context.js.map +0 -1
  36. package/dist/service.d.ts +0 -402
  37. package/dist/service.d.ts.map +0 -1
  38. package/dist/service.js +0 -1158
  39. package/dist/service.js.map +0 -1
  40. package/dist/storage.d.ts +0 -228
  41. package/dist/storage.d.ts.map +0 -1
  42. package/dist/storage.js +0 -218
  43. package/dist/storage.js.map +0 -1
  44. package/dist/template.d.ts +0 -10
  45. package/dist/template.d.ts.map +0 -1
  46. package/dist/template.js +0 -60
  47. package/dist/template.js.map +0 -1
  48. package/dist/ttl.d.ts +0 -144
  49. package/dist/ttl.d.ts.map +0 -1
  50. package/dist/ttl.js +0 -85
  51. package/dist/ttl.js.map +0 -1
  52. package/dist/types.d.ts +0 -1213
  53. package/dist/types.d.ts.map +0 -1
  54. package/dist/types.js +0 -39
  55. package/dist/types.js.map +0 -1
  56. package/dist/validation.d.ts +0 -156
  57. package/dist/validation.d.ts.map +0 -1
  58. package/dist/validation.js +0 -289
  59. package/dist/validation.js.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/service.ts"],"sourcesContent":["/**\n * @module service\n * @description Central service for managing agent-guided user journeys\n *\n * ## The Core Role\n *\n * The FormService is the **journey controller**. It ensures agents stay on\n * the path defined by form definitions, guiding users reliably to outcomes.\n *\n * Without this service, agents would:\n * - Forget what information they've collected\n * - Miss required fields\n * - Lose progress when users switch topics\n * - Have no way to resume interrupted journeys\n *\n * ## What It Does\n *\n * 1. **Defines Journeys**: Register form definitions (the maps)\n * 2. **Tracks Progress**: Manage sessions (where users are)\n * 3. **Validates Stops**: Ensure collected data meets requirements\n * 4. **Enables Pausing**: Stash journeys for later resumption\n * 5. **Records Completions**: Store submissions (outcomes achieved)\n * 6. **Guides Agents**: Provide context about what to do next\n * 7. **Manages Control Types**: Widget registry for simple, composite, external types\n *\n * ## Widget Registry (ControlType System)\n *\n * The FormService manages a registry of control types:\n *\n * - **Simple types** (text, number, email): Just validate/parse/format\n * - **Composite types** (address, payment setup): Have subcontrols\n * - **External types** (payment, signature): Require async confirmation\n *\n * Built-in types are registered at startup. Plugins can register custom types.\n *\n * WHY a registry:\n * - Decouples type definitions from form definitions\n * - Plugins can add domain-specific types (blockchain addresses, etc.)\n * - Override protection prevents accidental shadowing of built-ins\n *\n * ## Service Lifecycle\n *\n * ```\n * Plugin Init → FormService.start() → Register Builtins → Register Forms → Ready\n *\n * User Message → form evaluator → FormService.updateField() → Session Updated\n * → FormService.updateSubField() → Subfield Updated\n * → FormService.activateExternalField() → External Process Started\n * → FormService.submit() → Submission Created\n * → FormService.stash() → Session Stashed\n *\n * External Event → PaymentService → FormService.confirmExternalField() → Field Filled\n * ```\n *\n * ## State Management\n *\n * The service maintains two types of state:\n *\n * 1. **In-Memory**: Form definitions, control types (loaded at startup)\n * 2. **Persistent**: Sessions, submissions, autofill (via storage.ts)\n *\n * ## Consuming Plugin Pattern\n *\n * Plugins that use forms typically:\n *\n * ```typescript\n * // 1. Register form at plugin init\n * const formService = runtime.getService('FORM') as FormService;\n * formService.registerForm(myFormDefinition);\n *\n * // 2. Optionally register custom control types\n * formService.registerControlType({\n * id: 'payment',\n * getSubControls: () => [...],\n * activate: async (ctx) => {...},\n * });\n *\n * // 3. Start session when needed\n * await formService.startSession('my-form', entityId, roomId);\n *\n * // 4. Register hook workers for submission handling\n * runtime.registerTaskWorker({\n * name: 'handle_my_form_submission',\n * execute: async (runtime, options) => {\n * const { submission } = options;\n * // Process the submitted data\n * }\n * });\n * ```\n *\n * ## Error Handling\n *\n * Most methods throw on invalid state (e.g., session not found).\n * Callers should handle errors appropriately.\n */\n\nimport {\n type EventPayload,\n type IAgentRuntime,\n type JsonValue,\n logger,\n Service,\n type Task,\n type UUID,\n} from \"@elizaos/core\";\nimport { v4 as uuidv4 } from \"uuid\";\nimport { registerBuiltinTypes } from \"./builtins\";\nimport {\n getAutofillData,\n getSessionById,\n saveAutofillData,\n saveSubmission,\n getActiveSession as storageGetActiveSession,\n getAllActiveSessions as storageGetAllActiveSessions,\n getStashedSessions as storageGetStashedSessions,\n getSubmissions as storageGetSubmissions,\n saveSession as storageSaveSession,\n} from \"./storage\";\nimport type {\n ActivationContext,\n ControlType,\n ExternalActivation,\n FieldHistoryEntry,\n FieldState,\n FilledFieldSummary,\n FormContextState,\n FormControl,\n FormDefinition,\n FormSession,\n FormSubmission,\n MissingFieldSummary,\n PendingExternalFieldSummary,\n UncertainFieldSummary,\n} from \"./types\";\nimport { FORM_CONTROL_DEFAULTS, FORM_DEFINITION_DEFAULTS } from \"./types\";\nimport { formatValue, validateField } from \"./validation\";\n\n// ============================================================================\n// FORM SERVICE\n// ============================================================================\n\n/**\n * FormService - Central service for managing conversational forms.\n *\n * WHY a service:\n * - Services are singletons, one per agent\n * - Persist across conversations\n * - Accessible from actions, evaluators, providers\n *\n * WHY static `start` method:\n * - elizaOS service lifecycle pattern\n * - Async initialization support\n * - Returns Service interface\n */\nexport class FormService extends Service {\n /** Service type identifier for runtime.getService() */\n static serviceType = \"FORM\";\n\n /** Description shown in agent capabilities */\n capabilityDescription = \"Manages conversational forms for data collection\";\n\n /**\n * In-memory storage of form definitions.\n *\n * WHY Map:\n * - O(1) lookup by ID\n * - Forms are static after registration\n * - No persistence needed (re-registered on startup)\n */\n private forms: Map<string, FormDefinition> = new Map();\n\n /**\n * Control type registry.\n *\n * Built-in types are registered on start.\n * Plugins can register custom types.\n */\n private controlTypes: Map<string, ControlType> = new Map();\n\n /**\n * Start the FormService\n */\n static async start(runtime: IAgentRuntime): Promise<Service> {\n const service = new FormService(runtime);\n\n // Register built-in control types\n registerBuiltinTypes((type, options) =>\n service.registerControlType(type, options),\n );\n\n logger.info(\"[FormService] Started with built-in types\");\n return service;\n }\n\n /**\n * Stop the FormService\n */\n async stop(): Promise<void> {\n logger.info(\"[FormService] Stopped\");\n }\n\n // ============================================================================\n // FORM DEFINITION MANAGEMENT\n // ============================================================================\n\n /**\n * Register a form definition\n */\n registerForm(definition: FormDefinition): void {\n // Apply defaults\n const form: FormDefinition = {\n ...definition,\n version: definition.version ?? FORM_DEFINITION_DEFAULTS.version,\n status: definition.status ?? FORM_DEFINITION_DEFAULTS.status,\n ux: { ...FORM_DEFINITION_DEFAULTS.ux, ...definition.ux },\n ttl: { ...FORM_DEFINITION_DEFAULTS.ttl, ...definition.ttl },\n nudge: { ...FORM_DEFINITION_DEFAULTS.nudge, ...definition.nudge },\n debug: definition.debug ?? FORM_DEFINITION_DEFAULTS.debug,\n controls: definition.controls.map((control) => ({\n ...control,\n type: control.type || FORM_CONTROL_DEFAULTS.type,\n required: control.required ?? FORM_CONTROL_DEFAULTS.required,\n confirmThreshold:\n control.confirmThreshold ?? FORM_CONTROL_DEFAULTS.confirmThreshold,\n label: control.label || prettify(control.key),\n })),\n };\n\n this.forms.set(form.id, form);\n logger.debug(`[FormService] Registered form: ${form.id}`);\n }\n\n /**\n * Get a form definition by ID\n */\n getForm(formId: string): FormDefinition | undefined {\n return this.forms.get(formId);\n }\n\n /**\n * Get all registered forms\n */\n listForms(): FormDefinition[] {\n return Array.from(this.forms.values());\n }\n\n // ============================================================================\n // CONTROL TYPE REGISTRY\n // ============================================================================\n\n /**\n * Register a control type.\n *\n * Control types define how a field type behaves:\n * - Simple types: validate/parse/format\n * - Composite types: have subcontrols\n * - External types: have activate/deactivate for async processes\n *\n * Built-in types (text, number, email, etc.) are registered at startup\n * and protected from override unless explicitly allowed.\n *\n * @param type - The ControlType definition\n * @param options - Registration options\n * @param options.allowOverride - Allow overriding built-in types (default: false)\n *\n * @example\n * ```typescript\n * formService.registerControlType({\n * id: 'payment',\n * getSubControls: () => [\n * { key: 'amount', type: 'number', label: 'Amount', required: true },\n * { key: 'currency', type: 'select', label: 'Currency', required: true },\n * ],\n * activate: async (ctx) => {\n * const paymentService = ctx.runtime.getService('PAYMENT');\n * return paymentService.createPending(ctx.subValues);\n * },\n * });\n * ```\n */\n registerControlType(\n type: ControlType,\n options?: { allowOverride?: boolean },\n ): void {\n const existing = this.controlTypes.get(type.id);\n\n if (existing) {\n if (existing.builtin && !options?.allowOverride) {\n logger.warn(\n `[FormService] Cannot override builtin type '${type.id}' without allowOverride: true`,\n );\n return;\n }\n logger.warn(`[FormService] Overriding control type: ${type.id}`);\n }\n\n this.controlTypes.set(type.id, type);\n logger.debug(`[FormService] Registered control type: ${type.id}`);\n }\n\n /**\n * Get a control type by ID.\n *\n * @param typeId - The type ID to look up\n * @returns The ControlType or undefined if not found\n */\n getControlType(typeId: string): ControlType | undefined {\n return this.controlTypes.get(typeId);\n }\n\n /**\n * List all registered control types.\n *\n * @returns Array of all registered ControlTypes\n */\n listControlTypes(): ControlType[] {\n return Array.from(this.controlTypes.values());\n }\n\n /**\n * Check if a control type has subcontrols.\n *\n * @param typeId - The type ID to check\n * @returns true if the type has getSubControls method\n */\n isCompositeType(typeId: string): boolean {\n const type = this.controlTypes.get(typeId);\n return !!type?.getSubControls;\n }\n\n /**\n * Check if a control type is an external type.\n *\n * @param typeId - The type ID to check\n * @returns true if the type has activate method\n */\n isExternalType(typeId: string): boolean {\n const type = this.controlTypes.get(typeId);\n return !!type?.activate;\n }\n\n /**\n * Get subcontrols for a composite type.\n *\n * @param control - The parent control\n * @returns Array of subcontrols or empty array if not composite\n */\n getSubControls(control: FormControl): FormControl[] {\n const type = this.controlTypes.get(control.type);\n if (!type?.getSubControls) {\n return [];\n }\n return type.getSubControls(control, this.runtime);\n }\n\n // ============================================================================\n // SESSION MANAGEMENT\n // ============================================================================\n\n /**\n * Start a new form session\n */\n async startSession(\n formId: string,\n entityId: UUID,\n roomId: UUID,\n options?: {\n context?: Record<string, JsonValue>;\n initialValues?: Record<string, JsonValue>;\n locale?: string;\n },\n ): Promise<FormSession> {\n const form = this.getForm(formId);\n if (!form) {\n throw new Error(`Form not found: ${formId}`);\n }\n\n // Check for existing active session\n const existing = await storageGetActiveSession(\n this.runtime,\n entityId,\n roomId,\n );\n if (existing) {\n throw new Error(\n `Active session already exists for this user/room: ${existing.id}`,\n );\n }\n\n const now = Date.now();\n\n // Initialize field states\n const fields: Record<string, FieldState> = {};\n for (const control of form.controls) {\n if (options?.initialValues?.[control.key] !== undefined) {\n fields[control.key] = {\n status: \"filled\",\n value: options.initialValues[control.key],\n source: \"manual\",\n updatedAt: now,\n };\n } else if (control.defaultValue !== undefined) {\n fields[control.key] = {\n status: \"filled\",\n value: control.defaultValue,\n source: \"default\",\n updatedAt: now,\n };\n } else {\n fields[control.key] = { status: \"empty\" };\n }\n }\n\n // Calculate initial TTL\n const ttlDays = form.ttl?.minDays ?? 14;\n const expiresAt = now + ttlDays * 24 * 60 * 60 * 1000;\n\n const session: FormSession = {\n id: uuidv4(),\n formId,\n formVersion: form.version,\n entityId,\n roomId,\n status: \"active\",\n fields,\n history: [],\n context: options?.context,\n locale: options?.locale,\n effort: {\n interactionCount: 0,\n timeSpentMs: 0,\n firstInteractionAt: now,\n lastInteractionAt: now,\n },\n expiresAt,\n createdAt: now,\n updatedAt: now,\n };\n\n await storageSaveSession(this.runtime, session);\n\n // Execute onStart hook\n if (form.hooks?.onStart) {\n await this.executeHook(session, \"onStart\");\n }\n\n logger.debug(\n `[FormService] Started session ${session.id} for form ${formId}`,\n );\n\n return session;\n }\n\n /**\n * Get active session for entity in room\n */\n async getActiveSession(\n entityId: UUID,\n roomId: UUID,\n ): Promise<FormSession | null> {\n return storageGetActiveSession(this.runtime, entityId, roomId);\n }\n\n /**\n * Get all active sessions for entity (across all rooms)\n */\n async getAllActiveSessions(entityId: UUID): Promise<FormSession[]> {\n return storageGetAllActiveSessions(this.runtime, entityId);\n }\n\n /**\n * Get stashed sessions for entity\n */\n async getStashedSessions(entityId: UUID): Promise<FormSession[]> {\n return storageGetStashedSessions(this.runtime, entityId);\n }\n\n /**\n * Save a session\n */\n async saveSession(session: FormSession): Promise<void> {\n session.updatedAt = Date.now();\n await storageSaveSession(this.runtime, session);\n }\n\n // ============================================================================\n // FIELD UPDATES\n // ============================================================================\n\n /**\n * Update a field value\n */\n async updateField(\n sessionId: string,\n entityId: UUID,\n field: string,\n value: JsonValue,\n confidence: number,\n source: FieldState[\"source\"],\n messageId?: string,\n ): Promise<void> {\n const session = await getSessionById(this.runtime, entityId, sessionId);\n if (!session) {\n throw new Error(`Session not found: ${sessionId}`);\n }\n\n const form = this.getForm(session.formId);\n if (!form) {\n throw new Error(`Form not found: ${session.formId}`);\n }\n\n const control = form.controls.find((c) => c.key === field);\n if (!control) {\n throw new Error(`Field not found: ${field}`);\n }\n\n // Get old value for history\n const oldValue = session.fields[field]?.value;\n\n // Validate the value\n const validation = validateField(value, control);\n\n // Determine status based on confidence and validation\n let status: FieldState[\"status\"];\n if (!validation.valid) {\n status = \"invalid\";\n } else if (confidence < (control.confirmThreshold ?? 0.8)) {\n status = \"uncertain\";\n } else {\n status = \"filled\";\n }\n\n const now = Date.now();\n\n // Add to history for undo\n if (oldValue !== undefined) {\n const historyEntry: FieldHistoryEntry = {\n field,\n oldValue,\n newValue: value,\n timestamp: now,\n };\n session.history.push(historyEntry);\n\n // Limit history size\n const maxUndo = form.ux?.maxUndoSteps ?? 5;\n if (session.history.length > maxUndo) {\n session.history = session.history.slice(-maxUndo);\n }\n }\n\n // Update field state\n session.fields[field] = {\n status,\n value,\n confidence,\n source,\n messageId,\n updatedAt: now,\n error: !validation.valid ? validation.error : undefined,\n };\n\n // Update effort tracking\n session.effort.interactionCount++;\n session.effort.lastInteractionAt = now;\n session.effort.timeSpentMs = now - session.effort.firstInteractionAt;\n\n // Recalculate TTL\n session.expiresAt = this.calculateTTL(session);\n\n // Check if all required fields are filled\n const allRequiredFilled = this.checkAllRequiredFilled(session, form);\n if (allRequiredFilled && session.status === \"active\") {\n session.status = \"ready\";\n if (form.hooks?.onReady) {\n await this.executeHook(session, \"onReady\");\n }\n }\n\n session.updatedAt = now;\n await storageSaveSession(this.runtime, session);\n\n // Execute onFieldChange hook\n if (form.hooks?.onFieldChange) {\n const hookPayload: Record<string, JsonValue> = { field, value };\n if (oldValue !== undefined) {\n hookPayload.oldValue = oldValue;\n }\n await this.executeHook(session, \"onFieldChange\", hookPayload);\n }\n }\n\n /**\n * Undo the last field change\n */\n async undoLastChange(\n sessionId: string,\n entityId: UUID,\n ): Promise<{ field: string; restoredValue: JsonValue } | null> {\n const session = await getSessionById(this.runtime, entityId, sessionId);\n if (!session) {\n throw new Error(`Session not found: ${sessionId}`);\n }\n\n const form = this.getForm(session.formId);\n if (!form?.ux?.allowUndo) {\n return null;\n }\n\n const lastChange = session.history.pop();\n if (!lastChange) {\n return null;\n }\n\n // Restore the old value\n if (lastChange.oldValue !== undefined) {\n session.fields[lastChange.field] = {\n status: \"filled\",\n value: lastChange.oldValue,\n source: \"correction\",\n updatedAt: Date.now(),\n };\n } else {\n session.fields[lastChange.field] = { status: \"empty\" };\n }\n\n session.updatedAt = Date.now();\n await storageSaveSession(this.runtime, session);\n\n return { field: lastChange.field, restoredValue: lastChange.oldValue };\n }\n\n /**\n * Skip an optional field\n */\n async skipField(\n sessionId: string,\n entityId: UUID,\n field: string,\n ): Promise<boolean> {\n const session = await getSessionById(this.runtime, entityId, sessionId);\n if (!session) {\n throw new Error(`Session not found: ${sessionId}`);\n }\n\n const form = this.getForm(session.formId);\n if (!form?.ux?.allowSkip) {\n return false;\n }\n\n const control = form.controls.find((c) => c.key === field);\n if (!control) {\n return false;\n }\n\n // Can't skip required fields\n if (control.required) {\n return false;\n }\n\n session.fields[field] = {\n status: \"skipped\",\n updatedAt: Date.now(),\n };\n\n session.updatedAt = Date.now();\n await storageSaveSession(this.runtime, session);\n\n return true;\n }\n\n /**\n * Confirm an uncertain field value\n */\n async confirmField(\n sessionId: string,\n entityId: UUID,\n field: string,\n accepted: boolean,\n ): Promise<void> {\n const session = await getSessionById(this.runtime, entityId, sessionId);\n if (!session) {\n throw new Error(`Session not found: ${sessionId}`);\n }\n\n const fieldState = session.fields[field];\n if (!fieldState || fieldState.status !== \"uncertain\") {\n return;\n }\n\n const now = Date.now();\n\n if (accepted) {\n fieldState.status = \"filled\";\n fieldState.confirmedAt = now;\n } else {\n // Reset the field\n fieldState.status = \"empty\";\n fieldState.value = undefined;\n fieldState.confidence = undefined;\n }\n\n fieldState.updatedAt = now;\n session.updatedAt = now;\n await storageSaveSession(this.runtime, session);\n }\n\n // ============================================================================\n // SUBFIELD UPDATES (for composite types)\n // ============================================================================\n\n /**\n * Update a subfield value for a composite control type.\n *\n * Composite types (like payment, address) have subcontrols that must\n * all be filled before the parent field is complete.\n *\n * WHY separate from updateField:\n * - Subfields are stored in fieldState.subFields, not session.fields\n * - Parent field status depends on all subfields being filled\n * - Allows tracking subfield confidence/status independently\n *\n * @param sessionId - The session ID\n * @param entityId - The entity/user ID\n * @param parentField - The parent control key (e.g., \"payment\")\n * @param subField - The subcontrol key (e.g., \"amount\")\n * @param value - The extracted value\n * @param confidence - LLM confidence (0-1)\n * @param messageId - Optional message ID for audit\n */\n async updateSubField(\n sessionId: string,\n entityId: UUID,\n parentField: string,\n subField: string,\n value: JsonValue,\n confidence: number,\n messageId?: string,\n ): Promise<void> {\n const session = await getSessionById(this.runtime, entityId, sessionId);\n if (!session) {\n throw new Error(`Session not found: ${sessionId}`);\n }\n\n const form = this.getForm(session.formId);\n if (!form) {\n throw new Error(`Form not found: ${session.formId}`);\n }\n\n const parentControl = form.controls.find((c) => c.key === parentField);\n if (!parentControl) {\n throw new Error(`Parent field not found: ${parentField}`);\n }\n\n const controlType = this.getControlType(parentControl.type);\n if (!controlType?.getSubControls) {\n throw new Error(\n `Control type '${parentControl.type}' is not a composite type`,\n );\n }\n\n // Get subcontrols to find the subcontrol definition\n const subControls = controlType.getSubControls(parentControl, this.runtime);\n const subControl = subControls.find((c) => c.key === subField);\n if (!subControl) {\n throw new Error(`Subfield not found: ${subField} in ${parentField}`);\n }\n\n const now = Date.now();\n\n // Initialize parent field state if needed\n if (!session.fields[parentField]) {\n session.fields[parentField] = { status: \"empty\" };\n }\n if (!session.fields[parentField].subFields) {\n session.fields[parentField].subFields = {};\n }\n\n // Validate the subfield value\n let subFieldStatus: FieldState[\"status\"];\n let error: string | undefined;\n\n // Use control type's validate if available\n if (controlType.validate) {\n const result = controlType.validate(value, subControl);\n if (!result.valid) {\n subFieldStatus = \"invalid\";\n error = result.error;\n } else if (confidence < (subControl.confirmThreshold ?? 0.8)) {\n subFieldStatus = \"uncertain\";\n } else {\n subFieldStatus = \"filled\";\n }\n } else {\n // Fallback to basic validation\n const validation = validateField(value, subControl);\n if (!validation.valid) {\n subFieldStatus = \"invalid\";\n error = validation.error;\n } else if (confidence < (subControl.confirmThreshold ?? 0.8)) {\n subFieldStatus = \"uncertain\";\n } else {\n subFieldStatus = \"filled\";\n }\n }\n\n // Update the subfield state\n session.fields[parentField].subFields[subField] = {\n status: subFieldStatus,\n value,\n confidence,\n source: \"extraction\",\n messageId,\n updatedAt: now,\n error,\n };\n\n // Update effort tracking\n session.effort.interactionCount++;\n session.effort.lastInteractionAt = now;\n session.effort.timeSpentMs = now - session.effort.firstInteractionAt;\n\n session.updatedAt = now;\n await storageSaveSession(this.runtime, session);\n\n logger.debug(`[FormService] Updated subfield ${parentField}.${subField}`);\n }\n\n /**\n * Check if all subfields of a composite field are filled.\n *\n * @param session - The form session\n * @param parentField - The parent control key\n * @returns true if all required subfields are filled\n */\n areSubFieldsFilled(session: FormSession, parentField: string): boolean {\n const form = this.getForm(session.formId);\n if (!form) return false;\n\n const parentControl = form.controls.find((c) => c.key === parentField);\n if (!parentControl) return false;\n\n const controlType = this.getControlType(parentControl.type);\n if (!controlType?.getSubControls) return false;\n\n const subControls = controlType.getSubControls(parentControl, this.runtime);\n const subFields = session.fields[parentField]?.subFields || {};\n\n // Check if all required subfields are filled\n for (const subControl of subControls) {\n if (!subControl.required) continue;\n const subField = subFields[subControl.key];\n if (!subField || subField.status !== \"filled\") {\n return false;\n }\n }\n\n return true;\n }\n\n /**\n * Get the current subfield values for a composite field.\n *\n * @param session - The form session\n * @param parentField - The parent control key\n * @returns Record of subfield key to value\n */\n getSubFieldValues(\n session: FormSession,\n parentField: string,\n ): Record<string, JsonValue> {\n const subFields = session.fields[parentField]?.subFields || {};\n const values: Record<string, JsonValue> = {};\n for (const [key, state] of Object.entries(subFields)) {\n if (state.value !== undefined) {\n values[key] = state.value;\n }\n }\n return values;\n }\n\n // ============================================================================\n // EXTERNAL FIELD ACTIVATION\n // ============================================================================\n\n /**\n * Activate an external field.\n *\n * External types (payment, signature) require an async activation process.\n * This is called when all subcontrols are filled and the external process\n * should begin (e.g., generate payment address, show signing instructions).\n *\n * WHY this method:\n * - Decouples activation trigger from the widget itself\n * - Stores activation state in the session\n * - Provides a clear API for the evaluator to call\n *\n * @param sessionId - The session ID\n * @param entityId - The entity/user ID\n * @param field - The field key\n * @returns The activation details (instructions, reference, etc.)\n */\n async activateExternalField(\n sessionId: string,\n entityId: UUID,\n field: string,\n ): Promise<ExternalActivation> {\n const session = await getSessionById(this.runtime, entityId, sessionId);\n if (!session) {\n throw new Error(`Session not found: ${sessionId}`);\n }\n\n const form = this.getForm(session.formId);\n if (!form) {\n throw new Error(`Form not found: ${session.formId}`);\n }\n\n const control = form.controls.find((c) => c.key === field);\n if (!control) {\n throw new Error(`Field not found: ${field}`);\n }\n\n const controlType = this.getControlType(control.type);\n if (!controlType?.activate) {\n throw new Error(\n `Control type '${control.type}' does not support activation`,\n );\n }\n\n // Gather subfield values\n const subValues = this.getSubFieldValues(session, field);\n\n // Create activation context\n const context: ActivationContext = {\n runtime: this.runtime,\n session,\n control,\n subValues,\n };\n\n // Call widget's activate method\n const activation = await controlType.activate(context);\n\n const now = Date.now();\n\n // Store activation state in the field\n if (!session.fields[field]) {\n session.fields[field] = { status: \"empty\" };\n }\n\n session.fields[field].status = \"pending\";\n session.fields[field].externalState = {\n status: \"pending\",\n reference: activation.reference,\n instructions: activation.instructions,\n address: activation.address,\n activatedAt: now,\n };\n\n session.updatedAt = now;\n await storageSaveSession(this.runtime, session);\n\n logger.info(\n `[FormService] Activated external field ${field} with reference ${activation.reference}`,\n );\n\n return activation;\n }\n\n /**\n * Confirm an external field.\n *\n * Called by external services (payment, blockchain, etc.) when the\n * external process is complete (e.g., payment received, signature verified).\n *\n * WHY separate from confirmField:\n * - External confirmation includes external data (txId, etc.)\n * - Updates externalState, not just field status\n * - Emits events for other systems to react\n *\n * @param sessionId - The session ID\n * @param entityId - The entity/user ID\n * @param field - The field key\n * @param value - The final value to store (usually the confirmed data)\n * @param externalData - Additional data from the external source (txId, etc.)\n */\n async confirmExternalField(\n sessionId: string,\n entityId: UUID,\n field: string,\n value: JsonValue,\n externalData?: Record<string, JsonValue>,\n ): Promise<void> {\n const session = await getSessionById(this.runtime, entityId, sessionId);\n if (!session) {\n throw new Error(`Session not found: ${sessionId}`);\n }\n\n const fieldState = session.fields[field];\n if (!fieldState || fieldState.status !== \"pending\") {\n logger.warn(\n `[FormService] Cannot confirm field ${field}: not in pending state`,\n );\n return;\n }\n\n const now = Date.now();\n\n // Update field state\n fieldState.status = \"filled\";\n fieldState.value = value;\n fieldState.source = \"external\";\n fieldState.updatedAt = now;\n\n // Update external state\n if (fieldState.externalState) {\n fieldState.externalState.status = \"confirmed\";\n fieldState.externalState.confirmedAt = now;\n fieldState.externalState.externalData = externalData;\n }\n\n // Check if form is now ready\n const form = this.getForm(session.formId);\n if (form && this.checkAllRequiredFilled(session, form)) {\n if (session.status === \"active\") {\n session.status = \"ready\";\n if (form.hooks?.onReady) {\n await this.executeHook(session, \"onReady\");\n }\n }\n }\n\n session.updatedAt = now;\n await storageSaveSession(this.runtime, session);\n\n // Emit event for listeners\n try {\n await this.runtime.emitEvent(\"FORM_FIELD_CONFIRMED\", {\n runtime: this.runtime,\n sessionId,\n entityId,\n field,\n value,\n externalData,\n } as EventPayload);\n } catch (_error) {\n logger.debug(`[FormService] No event handler for FORM_FIELD_CONFIRMED`);\n }\n\n logger.info(`[FormService] Confirmed external field ${field}`);\n }\n\n /**\n * Cancel an external field activation.\n *\n * Called when the external process fails, times out, or user cancels.\n *\n * @param sessionId - The session ID\n * @param entityId - The entity/user ID\n * @param field - The field key\n * @param reason - Reason for cancellation\n */\n async cancelExternalField(\n sessionId: string,\n entityId: UUID,\n field: string,\n reason: string,\n ): Promise<void> {\n const session = await getSessionById(this.runtime, entityId, sessionId);\n if (!session) {\n throw new Error(`Session not found: ${sessionId}`);\n }\n\n const form = this.getForm(session.formId);\n const control = form?.controls.find((c) => c.key === field);\n const controlType = control ? this.getControlType(control.type) : undefined;\n\n // Call widget's deactivate if exists\n if (controlType?.deactivate && control) {\n try {\n await controlType.deactivate({\n runtime: this.runtime,\n session,\n control,\n subValues: this.getSubFieldValues(session, field),\n });\n } catch (error) {\n logger.error(\n `[FormService] Deactivate failed for ${field}: ${String(error)}`,\n );\n }\n }\n\n const fieldState = session.fields[field];\n if (fieldState) {\n // Keep subfields but reset external state\n fieldState.status = \"empty\";\n fieldState.error = reason;\n if (fieldState.externalState) {\n fieldState.externalState.status = \"failed\";\n }\n }\n\n session.updatedAt = Date.now();\n await storageSaveSession(this.runtime, session);\n\n // Emit event for listeners\n try {\n await this.runtime.emitEvent(\"FORM_FIELD_CANCELLED\", {\n runtime: this.runtime,\n sessionId,\n entityId,\n field,\n reason,\n } as EventPayload);\n } catch (_error) {\n logger.debug(`[FormService] No event handler for FORM_FIELD_CANCELLED`);\n }\n\n logger.info(`[FormService] Cancelled external field ${field}: ${reason}`);\n }\n\n // ============================================================================\n // LIFECYCLE\n // ============================================================================\n\n /**\n * Submit a form session\n */\n async submit(sessionId: string, entityId: UUID): Promise<FormSubmission> {\n const session = await getSessionById(this.runtime, entityId, sessionId);\n if (!session) {\n throw new Error(`Session not found: ${sessionId}`);\n }\n\n const form = this.getForm(session.formId);\n if (!form) {\n throw new Error(`Form not found: ${session.formId}`);\n }\n\n // Check all required fields are filled\n if (!this.checkAllRequiredFilled(session, form)) {\n throw new Error(\"Not all required fields are filled\");\n }\n\n const now = Date.now();\n\n // Build submission values\n const values: Record<string, JsonValue> = {};\n const mappedValues: Record<string, JsonValue> = {};\n const files: Record<string, NonNullable<FieldState[\"files\"]>> = {};\n\n for (const control of form.controls) {\n const fieldState = session.fields[control.key];\n if (fieldState?.value !== undefined) {\n values[control.key] = fieldState.value;\n const dbKey = control.dbbind || control.key;\n mappedValues[dbKey] = fieldState.value;\n }\n if (fieldState?.files) {\n files[control.key] = fieldState.files;\n }\n }\n\n const submission: FormSubmission = {\n id: uuidv4(),\n formId: session.formId,\n formVersion: session.formVersion,\n sessionId: session.id,\n entityId: session.entityId,\n values,\n mappedValues,\n files: Object.keys(files).length > 0 ? files : undefined,\n submittedAt: now,\n meta: session.meta,\n };\n\n // Save submission\n await saveSubmission(this.runtime, submission);\n\n // Update autofill data\n await saveAutofillData(this.runtime, entityId, session.formId, values);\n\n // Update session status\n session.status = \"submitted\";\n session.submittedAt = now;\n session.updatedAt = now;\n await storageSaveSession(this.runtime, session);\n\n // Execute onSubmit hook\n if (form.hooks?.onSubmit) {\n const submissionPayload = JSON.parse(\n JSON.stringify(submission),\n ) as JsonValue;\n await this.executeHook(session, \"onSubmit\", {\n submission: submissionPayload,\n });\n }\n\n logger.debug(`[FormService] Submitted session ${sessionId}`);\n\n return submission;\n }\n\n /**\n * Stash a session for later\n */\n async stash(sessionId: string, entityId: UUID): Promise<void> {\n const session = await getSessionById(this.runtime, entityId, sessionId);\n if (!session) {\n throw new Error(`Session not found: ${sessionId}`);\n }\n\n const form = this.getForm(session.formId);\n\n session.status = \"stashed\";\n session.updatedAt = Date.now();\n await storageSaveSession(this.runtime, session);\n\n // Execute onStash hook\n if (form?.hooks?.onCancel) {\n // Using onCancel for stash as well, could add separate hook\n }\n\n logger.debug(`[FormService] Stashed session ${sessionId}`);\n }\n\n /**\n * Restore a stashed session\n */\n async restore(sessionId: string, entityId: UUID): Promise<FormSession> {\n const session = await getSessionById(this.runtime, entityId, sessionId);\n if (!session) {\n throw new Error(`Session not found: ${sessionId}`);\n }\n\n if (session.status !== \"stashed\") {\n throw new Error(`Session is not stashed: ${session.status}`);\n }\n\n // Check for existing active session in the same room\n const existing = await storageGetActiveSession(\n this.runtime,\n entityId,\n session.roomId,\n );\n if (existing && existing.id !== sessionId) {\n throw new Error(`Active session already exists in room: ${existing.id}`);\n }\n\n session.status = \"active\";\n session.updatedAt = Date.now();\n\n // Recalculate TTL on restore\n session.expiresAt = this.calculateTTL(session);\n\n await storageSaveSession(this.runtime, session);\n\n logger.debug(`[FormService] Restored session ${sessionId}`);\n\n return session;\n }\n\n /**\n * Cancel a session\n */\n async cancel(\n sessionId: string,\n entityId: UUID,\n force = false,\n ): Promise<boolean> {\n const session = await getSessionById(this.runtime, entityId, sessionId);\n if (!session) {\n throw new Error(`Session not found: ${sessionId}`);\n }\n\n // Check if we should confirm cancellation\n if (\n !force &&\n this.shouldConfirmCancel(session) &&\n !session.cancelConfirmationAsked\n ) {\n session.cancelConfirmationAsked = true;\n session.updatedAt = Date.now();\n await storageSaveSession(this.runtime, session);\n return false; // Needs confirmation\n }\n\n const form = this.getForm(session.formId);\n\n session.status = \"cancelled\";\n session.updatedAt = Date.now();\n await storageSaveSession(this.runtime, session);\n\n // Execute onCancel hook\n if (form?.hooks?.onCancel) {\n await this.executeHook(session, \"onCancel\");\n }\n\n logger.debug(`[FormService] Cancelled session ${sessionId}`);\n\n return true;\n }\n\n // ============================================================================\n // SUBMISSIONS\n // ============================================================================\n\n /**\n * Get submissions for entity, optionally filtered by form ID\n */\n async getSubmissions(\n entityId: UUID,\n formId?: string,\n ): Promise<FormSubmission[]> {\n return storageGetSubmissions(this.runtime, entityId, formId);\n }\n\n // ============================================================================\n // AUTOFILL\n // ============================================================================\n\n /**\n * Get autofill data for a form\n */\n async getAutofill(\n entityId: UUID,\n formId: string,\n ): Promise<Record<string, JsonValue> | null> {\n const data = await getAutofillData(this.runtime, entityId, formId);\n return data?.values || null;\n }\n\n /**\n * Apply autofill to a session\n */\n async applyAutofill(session: FormSession): Promise<string[]> {\n const form = this.getForm(session.formId);\n if (!form?.ux?.allowAutofill) {\n return [];\n }\n\n const autofill = await getAutofillData(\n this.runtime,\n session.entityId,\n session.formId,\n );\n if (!autofill) {\n return [];\n }\n\n const appliedFields: string[] = [];\n const now = Date.now();\n\n for (const control of form.controls) {\n // Only autofill empty fields\n if (session.fields[control.key]?.status !== \"empty\") {\n continue;\n }\n\n const value = autofill.values[control.key];\n if (value !== undefined) {\n session.fields[control.key] = {\n status: \"filled\",\n value,\n source: \"autofill\",\n updatedAt: now,\n };\n appliedFields.push(control.key);\n }\n }\n\n if (appliedFields.length > 0) {\n session.updatedAt = now;\n await storageSaveSession(this.runtime, session);\n }\n\n return appliedFields;\n }\n\n // ============================================================================\n // CONTEXT HELPERS\n // ============================================================================\n\n /**\n * Get session context for provider\n */\n getSessionContext(session: FormSession): FormContextState {\n const form = this.getForm(session.formId);\n if (!form) {\n return {\n hasActiveForm: false,\n progress: 0,\n filledFields: [],\n missingRequired: [],\n uncertainFields: [],\n nextField: null,\n pendingExternalFields: [],\n };\n }\n\n const filledFields: FilledFieldSummary[] = [];\n const missingRequired: MissingFieldSummary[] = [];\n const uncertainFields: UncertainFieldSummary[] = [];\n const pendingExternalFields: PendingExternalFieldSummary[] = [];\n let nextField: FormControl | null = null;\n\n let filledCount = 0;\n let totalRequired = 0;\n\n for (const control of form.controls) {\n if (control.hidden) continue;\n\n const fieldState = session.fields[control.key];\n\n if (control.required) {\n totalRequired++;\n }\n\n if (fieldState?.status === \"filled\") {\n filledCount++;\n filledFields.push({\n key: control.key,\n label: control.label,\n displayValue: formatValue(fieldState.value ?? null, control),\n });\n } else if (fieldState?.status === \"pending\") {\n // External field waiting for confirmation\n if (fieldState.externalState) {\n pendingExternalFields.push({\n key: control.key,\n label: control.label,\n instructions:\n fieldState.externalState.instructions ||\n \"Waiting for confirmation...\",\n reference: fieldState.externalState.reference || \"\",\n activatedAt: fieldState.externalState.activatedAt || Date.now(),\n address: fieldState.externalState.address,\n });\n }\n // Don't set as nextField - we're waiting for external confirmation\n } else if (fieldState?.status === \"uncertain\") {\n uncertainFields.push({\n key: control.key,\n label: control.label,\n value: fieldState.value ?? null,\n confidence: fieldState.confidence ?? 0,\n });\n } else if (fieldState?.status === \"invalid\") {\n missingRequired.push({\n key: control.key,\n label: control.label,\n description: control.description,\n askPrompt: control.askPrompt,\n });\n if (!nextField) nextField = control;\n } else if (control.required && fieldState?.status !== \"skipped\") {\n missingRequired.push({\n key: control.key,\n label: control.label,\n description: control.description,\n askPrompt: control.askPrompt,\n });\n if (!nextField) nextField = control;\n } else if (!nextField && fieldState?.status === \"empty\") {\n nextField = control;\n }\n }\n\n const progress =\n totalRequired > 0 ? Math.round((filledCount / totalRequired) * 100) : 100;\n\n return {\n hasActiveForm: true,\n formId: session.formId,\n formName: form.name,\n progress,\n filledFields,\n missingRequired,\n uncertainFields,\n nextField,\n status: session.status,\n pendingCancelConfirmation:\n session.cancelConfirmationAsked && session.status === \"active\",\n pendingExternalFields,\n };\n }\n\n /**\n * Get current values from session\n */\n getValues(session: FormSession): Record<string, JsonValue> {\n const values: Record<string, JsonValue> = {};\n for (const [key, state] of Object.entries(session.fields)) {\n if (state.value !== undefined) {\n values[key] = state.value;\n }\n }\n return values;\n }\n\n /**\n * Get mapped values (using dbbind)\n */\n getMappedValues(session: FormSession): Record<string, JsonValue> {\n const form = this.getForm(session.formId);\n if (!form) return {};\n\n const values: Record<string, JsonValue> = {};\n for (const control of form.controls) {\n const state = session.fields[control.key];\n if (state?.value !== undefined) {\n const key = control.dbbind || control.key;\n values[key] = state.value;\n }\n }\n return values;\n }\n\n // ============================================================================\n // TTL & EFFORT\n // ============================================================================\n\n /**\n * Calculate TTL based on effort\n */\n calculateTTL(session: FormSession): number {\n const form = this.getForm(session.formId);\n const config = form?.ttl || {};\n\n const minDays = config.minDays ?? 14;\n const maxDays = config.maxDays ?? 90;\n const multiplier = config.effortMultiplier ?? 0.5;\n\n const minutesSpent = session.effort.timeSpentMs / 60000;\n const effortDays = minutesSpent * multiplier;\n\n const ttlDays = Math.min(maxDays, Math.max(minDays, effortDays));\n return Date.now() + ttlDays * 24 * 60 * 60 * 1000;\n }\n\n /**\n * Check if cancel should require confirmation\n */\n shouldConfirmCancel(session: FormSession): boolean {\n const minEffortMs = 5 * 60 * 1000; // 5 minutes\n return session.effort.timeSpentMs > minEffortMs;\n }\n\n // ============================================================================\n // HOOKS\n // ============================================================================\n\n /**\n * Execute a form hook\n */\n private async executeHook(\n session: FormSession,\n hookName: keyof NonNullable<FormDefinition[\"hooks\"]>,\n options?: Record<string, JsonValue>,\n ): Promise<void> {\n const form = this.getForm(session.formId);\n const workerName = form?.hooks?.[hookName];\n\n if (!workerName) return;\n\n const worker = this.runtime.getTaskWorker(workerName);\n if (!worker) {\n logger.warn(`[FormService] Hook worker not found: ${workerName}`);\n return;\n }\n\n try {\n // Create a minimal task object for hook execution\n const task: Task = {\n id: session.id as UUID,\n name: workerName,\n roomId: session.roomId,\n entityId: session.entityId,\n tags: [],\n };\n await worker.execute(\n this.runtime,\n {\n session,\n form,\n ...options,\n },\n task,\n );\n } catch (error) {\n logger.error(\n `[FormService] Hook execution failed: ${hookName}`,\n String(error),\n );\n }\n }\n\n // ============================================================================\n // HELPERS\n // ============================================================================\n\n /**\n * Check if all required fields are filled\n */\n private checkAllRequiredFilled(\n session: FormSession,\n form: FormDefinition,\n ): boolean {\n for (const control of form.controls) {\n if (!control.required) continue;\n\n const fieldState = session.fields[control.key];\n if (\n !fieldState ||\n fieldState.status === \"empty\" ||\n fieldState.status === \"invalid\"\n ) {\n return false;\n }\n }\n return true;\n }\n}\n\n// ============================================================================\n// UTILITY FUNCTIONS\n// ============================================================================\n\n/**\n * Convert snake_case or kebab-case to Title Case\n */\nfunction prettify(key: string): string {\n return key.replace(/[-_]/g, \" \").replace(/\\b\\w/g, (c) => c.toUpperCase());\n}\n"],"mappings":"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","names":[]}
package/dist/storage.d.ts DELETED
@@ -1,228 +0,0 @@
1
- /**
2
- * @module storage
3
- * @description Component-based persistence for form data
4
- *
5
- * ## Design Rationale
6
- *
7
- * Form data is stored using elizaOS's Component system because:
8
- *
9
- * 1. **Entity-Scoped**: Components belong to entities (users).
10
- * This naturally scopes form data per-user.
11
- *
12
- * 2. **Typed Storage**: Component type field allows different kinds
13
- * of form data (sessions, submissions, autofill).
14
- *
15
- * 3. **No Custom Schema**: Uses existing elizaOS infrastructure,
16
- * no need to create database tables.
17
- *
18
- * 4. **Room Scoping**: Component type includes roomId for session
19
- * isolation across rooms.
20
- *
21
- * ## Storage Strategy
22
- *
23
- * ### Sessions
24
- * - Stored as components with type: `form_session:{roomId}`
25
- * - One active session per user per room
26
- * - Scoping ensures different rooms have different contexts
27
- *
28
- * ### Submissions
29
- * - Stored as components with type: `form_submission:{formId}:{submissionId}`
30
- * - Immutable records of completed forms
31
- * - Multiple submissions per user (if form allows)
32
- *
33
- * ### Autofill
34
- * - Stored as components with type: `form_autofill:{formId}`
35
- * - One autofill record per user per form
36
- * - Updated on each submission
37
- *
38
- * ## Limitations
39
- *
40
- * The current implementation has limitations:
41
- *
42
- * 1. **No Cross-Entity Queries**: Can't efficiently find all stale
43
- * sessions across all users. This affects nudge system.
44
- *
45
- * 2. **No Indexes**: Component queries are sequential scans.
46
- * For high-volume usage, consider database-level optimizations.
47
- *
48
- * These are acceptable for v1 but noted for future improvement.
49
- */
50
- import type { IAgentRuntime, JsonValue, UUID } from "@elizaos/core";
51
- import type { FormAutofillData, FormSession, FormSubmission } from "./types";
52
- /**
53
- * Get active form session for entity in a specific room.
54
- *
55
- * WHY room-scoped:
56
- * - User might chat in multiple rooms simultaneously
57
- * - Each room conversation should have its own form context
58
- * - Discord DM form shouldn't interfere with Telegram form
59
- *
60
- * WHY active/ready filter:
61
- * - Stashed, submitted, cancelled, expired sessions are not "active"
62
- * - User would need to restore stashed sessions
63
- *
64
- * @param runtime - Agent runtime for database access
65
- * @param entityId - User's entity ID
66
- * @param roomId - The room to check for active session
67
- * @returns Active session or null if none
68
- */
69
- export declare function getActiveSession(runtime: IAgentRuntime, entityId: UUID, roomId: UUID): Promise<FormSession | null>;
70
- /**
71
- * Get all active sessions for an entity (across all rooms).
72
- *
73
- * WHY this exists:
74
- * - For "you have forms in progress" notifications
75
- * - For session management UI
76
- * - Not commonly used in normal flow
77
- *
78
- * @param runtime - Agent runtime for database access
79
- * @param entityId - User's entity ID
80
- * @returns Array of active sessions (may be empty)
81
- */
82
- export declare function getAllActiveSessions(runtime: IAgentRuntime, entityId: UUID): Promise<FormSession[]>;
83
- /**
84
- * Get stashed sessions for an entity.
85
- *
86
- * WHY stashed is separate from active:
87
- * - Stashed sessions are "saved for later"
88
- * - User must explicitly restore them
89
- * - Different UX from active sessions
90
- *
91
- * @param runtime - Agent runtime for database access
92
- * @param entityId - User's entity ID
93
- * @returns Array of stashed sessions (may be empty)
94
- */
95
- export declare function getStashedSessions(runtime: IAgentRuntime, entityId: UUID): Promise<FormSession[]>;
96
- /**
97
- * Get a session by ID.
98
- *
99
- * WHY by ID:
100
- * - Needed for operations on specific session
101
- * - Session ID is stable across room changes
102
- * - Used by stash/restore when session roomId changes
103
- *
104
- * @param runtime - Agent runtime for database access
105
- * @param entityId - User's entity ID
106
- * @param sessionId - The session ID to find
107
- * @returns The session or null if not found
108
- */
109
- export declare function getSessionById(runtime: IAgentRuntime, entityId: UUID, sessionId: string): Promise<FormSession | null>;
110
- /**
111
- * Save a form session.
112
- *
113
- * Creates new component if none exists, updates otherwise.
114
- *
115
- * WHY upsert pattern:
116
- * - Session is created once, updated many times
117
- * - Single function handles both cases
118
- * - Avoids race conditions
119
- *
120
- * @param runtime - Agent runtime for database access
121
- * @param session - Session to save
122
- */
123
- export declare function saveSession(runtime: IAgentRuntime, session: FormSession): Promise<void>;
124
- /**
125
- * Delete a session.
126
- *
127
- * WHY delete:
128
- * - Cleanup after submission/cancellation/expiry
129
- * - Frees up storage
130
- * - Note: Usually we just change status instead of deleting
131
- *
132
- * @param runtime - Agent runtime for database access
133
- * @param session - Session to delete
134
- */
135
- export declare function deleteSession(runtime: IAgentRuntime, session: FormSession): Promise<void>;
136
- /**
137
- * Save a form submission.
138
- *
139
- * Submissions are immutable records. Always creates new component.
140
- *
141
- * WHY new component per submission:
142
- * - Submissions are immutable
143
- * - Multiple submissions allowed (if form permits)
144
- * - Historical record keeping
145
- *
146
- * @param runtime - Agent runtime for database access
147
- * @param submission - Submission to save
148
- */
149
- export declare function saveSubmission(runtime: IAgentRuntime, submission: FormSubmission): Promise<void>;
150
- /**
151
- * Get submissions for an entity, optionally filtered by form ID.
152
- *
153
- * WHY optional formId:
154
- * - List all submissions: no formId
155
- * - List submissions for specific form: with formId
156
- *
157
- * @param runtime - Agent runtime for database access
158
- * @param entityId - User's entity ID
159
- * @param formId - Optional form ID filter
160
- * @returns Array of submissions, newest first
161
- */
162
- export declare function getSubmissions(runtime: IAgentRuntime, entityId: UUID, formId?: string): Promise<FormSubmission[]>;
163
- /**
164
- * Get a specific submission by ID.
165
- *
166
- * @param runtime - Agent runtime for database access
167
- * @param entityId - User's entity ID
168
- * @param submissionId - The submission ID to find
169
- * @returns The submission or null if not found
170
- */
171
- export declare function getSubmissionById(runtime: IAgentRuntime, entityId: UUID, submissionId: string): Promise<FormSubmission | null>;
172
- /**
173
- * Get autofill data for a user's form.
174
- *
175
- * WHY autofill:
176
- * - Users filling repeat forms want saved values
177
- * - Reduces friction for common fields (name, email, address)
178
- *
179
- * @param runtime - Agent runtime for database access
180
- * @param entityId - User's entity ID
181
- * @param formId - Form definition ID
182
- * @returns Autofill data or null if none saved
183
- */
184
- export declare function getAutofillData(runtime: IAgentRuntime, entityId: UUID, formId: string): Promise<FormAutofillData | null>;
185
- /**
186
- * Save autofill data for a user's form.
187
- *
188
- * Overwrites existing autofill data for the form.
189
- *
190
- * WHY overwrite:
191
- * - Most recent submission has most current data
192
- * - User's email might have changed
193
- * - Only one autofill record per form needed
194
- *
195
- * @param runtime - Agent runtime for database access
196
- * @param entityId - User's entity ID
197
- * @param formId - Form definition ID
198
- * @param values - Field values to save for autofill
199
- */
200
- export declare function saveAutofillData(runtime: IAgentRuntime, entityId: UUID, formId: string, values: Record<string, JsonValue>): Promise<void>;
201
- /**
202
- * Get all stale sessions (for nudge system).
203
- *
204
- * LIMITATION: This requires iterating over all entities, which is not
205
- * efficient with current elizaOS component system. In production,
206
- * this would need a database-level query.
207
- *
208
- * WHY this is here:
209
- * - Documents the need for this functionality
210
- * - Placeholder for future implementation
211
- * - Nudge system can call this when entity context is available
212
- *
213
- * @param runtime - Agent runtime for database access
214
- * @param afterInactiveMs - Inactivity threshold in milliseconds
215
- * @returns Array of stale sessions (currently returns empty array)
216
- */
217
- export declare function getStaleSessions(runtime: IAgentRuntime, _afterInactiveMs: number): Promise<FormSession[]>;
218
- /**
219
- * Get sessions expiring within a time window.
220
- *
221
- * Same limitation as getStaleSessions.
222
- *
223
- * @param runtime - Agent runtime for database access
224
- * @param withinMs - Time window in milliseconds
225
- * @returns Array of expiring sessions (currently returns empty array)
226
- */
227
- export declare function getExpiringSessions(runtime: IAgentRuntime, _withinMs: number): Promise<FormSession[]>;
228
- //# sourceMappingURL=storage.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../src/storage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AAEH,OAAO,KAAK,EAAa,aAAa,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAE/E,OAAO,KAAK,EAAE,gBAAgB,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AA0D7E;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,gBAAgB,CACpC,OAAO,EAAE,aAAa,EACtB,QAAQ,EAAE,IAAI,EACd,MAAM,EAAE,IAAI,GACX,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAkB7B;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,aAAa,EACtB,QAAQ,EAAE,IAAI,GACb,OAAO,CAAC,WAAW,EAAE,CAAC,CAiBxB;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,aAAa,EACtB,QAAQ,EAAE,IAAI,GACb,OAAO,CAAC,WAAW,EAAE,CAAC,CAgBxB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,cAAc,CAClC,OAAO,EAAE,aAAa,EACtB,QAAQ,EAAE,IAAI,EACd,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAe7B;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,WAAW,CAC/B,OAAO,EAAE,aAAa,EACtB,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC,IAAI,CAAC,CAyBf;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,aAAa,CACjC,OAAO,EAAE,aAAa,EACtB,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC,IAAI,CAAC,CAOf;AAMD;;;;;;;;;;;;GAYG;AACH,wBAAsB,cAAc,CAClC,OAAO,EAAE,aAAa,EACtB,UAAU,EAAE,cAAc,GACzB,OAAO,CAAC,IAAI,CAAC,CAmBf;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,cAAc,CAClC,OAAO,EAAE,aAAa,EACtB,QAAQ,EAAE,IAAI,EACd,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,cAAc,EAAE,CAAC,CAqB3B;AAED;;;;;;;GAOG;AACH,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,aAAa,EACtB,QAAQ,EAAE,IAAI,EACd,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAehC;AAMD;;;;;;;;;;;GAWG;AACH,wBAAsB,eAAe,CACnC,OAAO,EAAE,aAAa,EACtB,QAAQ,EAAE,IAAI,EACd,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAOlC;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,gBAAgB,CACpC,OAAO,EAAE,aAAa,EACtB,QAAQ,EAAE,IAAI,EACd,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAChC,OAAO,CAAC,IAAI,CAAC,CA6Bf;AAMD;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,gBAAgB,CACpC,OAAO,EAAE,aAAa,EACtB,gBAAgB,EAAE,MAAM,GACvB,OAAO,CAAC,WAAW,EAAE,CAAC,CAQxB;AAED;;;;;;;;GAQG;AACH,wBAAsB,mBAAmB,CACvC,OAAO,EAAE,aAAa,EACtB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,WAAW,EAAE,CAAC,CAKxB"}
package/dist/storage.js DELETED
@@ -1,218 +0,0 @@
1
- import { v4 as uuidv4 } from "uuid";
2
- import {
3
- FORM_AUTOFILL_COMPONENT,
4
- FORM_SESSION_COMPONENT,
5
- FORM_SUBMISSION_COMPONENT
6
- } from "./types";
7
- const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
8
- const resolveComponentContext = async (runtime, roomId) => {
9
- if (roomId) {
10
- const room = await runtime.getRoom(roomId);
11
- return { roomId, worldId: room?.worldId ?? runtime.agentId };
12
- }
13
- return { roomId: runtime.agentId, worldId: runtime.agentId };
14
- };
15
- const isFormSession = (data) => {
16
- if (!isRecord(data)) return false;
17
- return typeof data.id === "string" && typeof data.formId === "string" && typeof data.entityId === "string" && typeof data.roomId === "string";
18
- };
19
- const isFormSubmission = (data) => {
20
- if (!isRecord(data)) return false;
21
- return typeof data.id === "string" && typeof data.formId === "string" && typeof data.sessionId === "string" && typeof data.entityId === "string";
22
- };
23
- const isFormAutofillData = (data) => {
24
- if (!isRecord(data)) return false;
25
- return typeof data.formId === "string" && typeof data.updatedAt === "number" && typeof data.values === "object";
26
- };
27
- async function getActiveSession(runtime, entityId, roomId) {
28
- const component = await runtime.getComponent(
29
- entityId,
30
- `${FORM_SESSION_COMPONENT}:${roomId}`
31
- );
32
- if (!component?.data || !isFormSession(component.data)) return null;
33
- const session = component.data;
34
- if (session.status === "active" || session.status === "ready") {
35
- return session;
36
- }
37
- return null;
38
- }
39
- async function getAllActiveSessions(runtime, entityId) {
40
- const components = await runtime.getComponents(entityId);
41
- const sessions = [];
42
- for (const component of components) {
43
- if (component.type.startsWith(`${FORM_SESSION_COMPONENT}:`)) {
44
- if (component.data && isFormSession(component.data)) {
45
- const session = component.data;
46
- if (session.status === "active" || session.status === "ready") {
47
- sessions.push(session);
48
- }
49
- }
50
- }
51
- }
52
- return sessions;
53
- }
54
- async function getStashedSessions(runtime, entityId) {
55
- const components = await runtime.getComponents(entityId);
56
- const sessions = [];
57
- for (const component of components) {
58
- if (component.type.startsWith(`${FORM_SESSION_COMPONENT}:`)) {
59
- if (component.data && isFormSession(component.data)) {
60
- const session = component.data;
61
- if (session.status === "stashed") {
62
- sessions.push(session);
63
- }
64
- }
65
- }
66
- }
67
- return sessions;
68
- }
69
- async function getSessionById(runtime, entityId, sessionId) {
70
- const components = await runtime.getComponents(entityId);
71
- for (const component of components) {
72
- if (component.type.startsWith(`${FORM_SESSION_COMPONENT}:`)) {
73
- if (component.data && isFormSession(component.data)) {
74
- const session = component.data;
75
- if (session.id === sessionId) {
76
- return session;
77
- }
78
- }
79
- }
80
- }
81
- return null;
82
- }
83
- async function saveSession(runtime, session) {
84
- const componentType = `${FORM_SESSION_COMPONENT}:${session.roomId}`;
85
- const existing = await runtime.getComponent(session.entityId, componentType);
86
- const context = await resolveComponentContext(runtime, session.roomId);
87
- const resolvedWorldId = existing?.worldId ?? context.worldId;
88
- const component = {
89
- id: existing?.id || uuidv4(),
90
- entityId: session.entityId,
91
- agentId: runtime.agentId,
92
- roomId: session.roomId,
93
- // WHY preserve worldId: Avoids breaking existing component relationships
94
- worldId: resolvedWorldId,
95
- sourceEntityId: runtime.agentId,
96
- type: componentType,
97
- createdAt: existing?.createdAt || Date.now(),
98
- // Store session as component data
99
- data: JSON.parse(JSON.stringify(session))
100
- };
101
- if (existing) {
102
- await runtime.updateComponent(component);
103
- } else {
104
- await runtime.createComponent(component);
105
- }
106
- }
107
- async function deleteSession(runtime, session) {
108
- const componentType = `${FORM_SESSION_COMPONENT}:${session.roomId}`;
109
- const existing = await runtime.getComponent(session.entityId, componentType);
110
- if (existing) {
111
- await runtime.deleteComponent(existing.id);
112
- }
113
- }
114
- async function saveSubmission(runtime, submission) {
115
- const componentType = `${FORM_SUBMISSION_COMPONENT}:${submission.formId}:${submission.id}`;
116
- const context = await resolveComponentContext(runtime);
117
- const component = {
118
- id: uuidv4(),
119
- entityId: submission.entityId,
120
- agentId: runtime.agentId,
121
- roomId: context.roomId,
122
- worldId: context.worldId,
123
- sourceEntityId: runtime.agentId,
124
- type: componentType,
125
- createdAt: submission.submittedAt,
126
- data: JSON.parse(JSON.stringify(submission))
127
- };
128
- await runtime.createComponent(component);
129
- }
130
- async function getSubmissions(runtime, entityId, formId) {
131
- const components = await runtime.getComponents(entityId);
132
- const submissions = [];
133
- const prefix = formId ? `${FORM_SUBMISSION_COMPONENT}:${formId}:` : `${FORM_SUBMISSION_COMPONENT}:`;
134
- for (const component of components) {
135
- if (component.type.startsWith(prefix)) {
136
- if (component.data && isFormSubmission(component.data)) {
137
- submissions.push(component.data);
138
- }
139
- }
140
- }
141
- submissions.sort((a, b) => b.submittedAt - a.submittedAt);
142
- return submissions;
143
- }
144
- async function getSubmissionById(runtime, entityId, submissionId) {
145
- const components = await runtime.getComponents(entityId);
146
- for (const component of components) {
147
- if (component.type.startsWith(`${FORM_SUBMISSION_COMPONENT}:`)) {
148
- if (component.data && isFormSubmission(component.data)) {
149
- const submission = component.data;
150
- if (submission.id === submissionId) {
151
- return submission;
152
- }
153
- }
154
- }
155
- }
156
- return null;
157
- }
158
- async function getAutofillData(runtime, entityId, formId) {
159
- const componentType = `${FORM_AUTOFILL_COMPONENT}:${formId}`;
160
- const component = await runtime.getComponent(entityId, componentType);
161
- if (!component?.data || !isFormAutofillData(component.data)) return null;
162
- return component.data;
163
- }
164
- async function saveAutofillData(runtime, entityId, formId, values) {
165
- const componentType = `${FORM_AUTOFILL_COMPONENT}:${formId}`;
166
- const existing = await runtime.getComponent(entityId, componentType);
167
- const context = await resolveComponentContext(runtime);
168
- const resolvedWorldId = existing?.worldId ?? context.worldId;
169
- const data = {
170
- formId,
171
- values,
172
- updatedAt: Date.now()
173
- };
174
- const component = {
175
- id: existing?.id || uuidv4(),
176
- entityId,
177
- agentId: runtime.agentId,
178
- roomId: context.roomId,
179
- worldId: resolvedWorldId,
180
- sourceEntityId: runtime.agentId,
181
- type: componentType,
182
- createdAt: existing?.createdAt || Date.now(),
183
- data: JSON.parse(JSON.stringify(data))
184
- };
185
- if (existing) {
186
- await runtime.updateComponent(component);
187
- } else {
188
- await runtime.createComponent(component);
189
- }
190
- }
191
- async function getStaleSessions(runtime, _afterInactiveMs) {
192
- runtime.logger.warn(
193
- "getStaleSessions requires entity iteration - not implemented"
194
- );
195
- return [];
196
- }
197
- async function getExpiringSessions(runtime, _withinMs) {
198
- runtime.logger.warn(
199
- "getExpiringSessions requires entity iteration - not implemented"
200
- );
201
- return [];
202
- }
203
- export {
204
- deleteSession,
205
- getActiveSession,
206
- getAllActiveSessions,
207
- getAutofillData,
208
- getExpiringSessions,
209
- getSessionById,
210
- getStaleSessions,
211
- getStashedSessions,
212
- getSubmissionById,
213
- getSubmissions,
214
- saveAutofillData,
215
- saveSession,
216
- saveSubmission
217
- };
218
- //# sourceMappingURL=storage.js.map