@elizaos/plugin-form 2.0.0-alpha.3 → 2.0.0-alpha.4

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/types.d.ts DELETED
@@ -1,1186 +0,0 @@
1
- /**
2
- * @module types
3
- * @description Core type definitions for the Form Plugin
4
- *
5
- * ## The Core Insight
6
- *
7
- * Forms are **guardrails for agent-guided user journeys**.
8
- *
9
- * Without structure, agents wander. They forget what they're collecting,
10
- * miss required information, and can't reliably guide users to outcomes.
11
- * These types define the rails that keep agents on track.
12
- *
13
- * - **FormDefinition** = The journey map (what stops are required)
14
- * - **FormControl** = A stop on the journey (what info to collect)
15
- * - **FormSession** = Progress through the journey (where we are)
16
- * - **FormSubmission** = Journey complete (the outcome)
17
- *
18
- * ## Design Principles
19
- *
20
- * 1. **Agent-Native**: Designed for conversational, asynchronous interactions.
21
- * No form UI - the agent extracts data and guides the conversation.
22
- *
23
- * 2. **Future-Compatible**: Many fields are optional with sensible defaults.
24
- * The `meta` field on most interfaces allows arbitrary extension.
25
- *
26
- * 3. **Scoped Sessions**: Sessions are keyed by (entityId + roomId) because
27
- * a user might be on different journeys in different rooms.
28
- *
29
- * 4. **Effort-Aware**: Form data is retained based on user effort invested.
30
- * Someone who spent 2 hours deserves longer retention than 2 minutes.
31
- *
32
- * 5. **TypeScript-First**: Types use discriminated unions, generics, and
33
- * template literals for excellent IDE support and type safety.
34
- */
35
- import type { JsonValue, UUID } from "@elizaos/core";
36
- /**
37
- * Select/choice option for select-type fields.
38
- *
39
- * WHY separate from simple string[]:
40
- * - Labels can differ from values (display "United States", submit "US")
41
- * - Optional description enables rich select UIs in future
42
- * - Allows localization of labels without changing values
43
- */
44
- export interface FormControlOption {
45
- value: string;
46
- label: string;
47
- description?: string;
48
- }
49
- /**
50
- * File upload configuration.
51
- *
52
- * WHY separate interface:
53
- * - File uploads have unique concerns (size, type, count)
54
- * - Not all controls need these options
55
- * - Allows file-specific validation without polluting base control
56
- */
57
- export interface FormControlFileOptions {
58
- /** MIME type patterns, e.g., ['image/*', 'application/pdf'] */
59
- accept?: string[];
60
- /** Maximum file size in bytes */
61
- maxSize?: number;
62
- /** Maximum number of files (for multiple uploads) */
63
- maxFiles?: number;
64
- }
65
- /**
66
- * Conditional field dependency.
67
- *
68
- * WHY this exists:
69
- * - Some fields only make sense if another field has a value
70
- * - Example: "State" only relevant if "Country" is "US"
71
- * - The agent should skip asking about dependent fields until parent is filled
72
- */
73
- export interface FormControlDependency {
74
- /** Key of the field this one depends on */
75
- field: string;
76
- /** When should this field be shown/asked */
77
- condition: "exists" | "equals" | "not_equals";
78
- /** Value to compare against for equals/not_equals */
79
- value?: JsonValue;
80
- }
81
- /**
82
- * UI hints for future frontends.
83
- *
84
- * WHY include UI hints in an agent-native form:
85
- * - Forms may eventually render in GUI
86
- * - Provides grouping hints to the agent for logical conversation flow
87
- * - Widget hints allow custom input components
88
- */
89
- export interface FormControlUI {
90
- /** Section name for grouping fields */
91
- section?: string;
92
- /** Display order within section */
93
- order?: number;
94
- /** Placeholder text for input fields */
95
- placeholder?: string;
96
- /** Help text shown below input */
97
- helpText?: string;
98
- /** Custom widget type identifier */
99
- widget?: string;
100
- }
101
- /**
102
- * Localization for a field.
103
- *
104
- * WHY per-field i18n:
105
- * - Allows gradual localization (not all-or-nothing)
106
- * - Agent prompts need localized versions too
107
- * - Keeps localization close to the field it affects
108
- */
109
- export interface FormControlI18n {
110
- label?: string;
111
- description?: string;
112
- askPrompt?: string;
113
- helpText?: string;
114
- }
115
- /**
116
- * FormControl - The central field abstraction
117
- *
118
- * This is the heart of the form system. Each FormControl defines:
119
- * - What data to collect (key, type)
120
- * - How to validate it (pattern, min/max, required)
121
- * - How the agent should ask for it (askPrompt, extractHints)
122
- * - How to store it (dbbind)
123
- *
124
- * WHY such a rich interface:
125
- * - Agent needs context to extract values intelligently
126
- * - Validation needs to happen at extraction time, not just submission
127
- * - Multiple systems (agent, storage, UI) need different views of the same field
128
- *
129
- * WHY `type` is a string, not an enum:
130
- * - Allows custom types without changing core code
131
- * - Plugins can register handlers for domain-specific types
132
- * - Example: 'solana_address', 'evm_address', 'phone'
133
- */
134
- export interface FormControl {
135
- /** Unique key within the form. Used in values object. */
136
- key: string;
137
- /** Human-readable label. Shown to user if not using askPrompt. */
138
- label: string;
139
- /**
140
- * Field type. Built-in types: 'text', 'number', 'email', 'boolean', 'select', 'date', 'file'.
141
- * Custom types can be registered via FormService.registerType().
142
- */
143
- type: string;
144
- /** If true, form cannot be submitted without this field. Default: false */
145
- required?: boolean;
146
- /** If true, accepts array of values. Default: false */
147
- multiple?: boolean;
148
- /** If true, value cannot be changed after initial set. Default: false */
149
- readonly?: boolean;
150
- /** If true, extract silently but never ask directly. Default: false */
151
- hidden?: boolean;
152
- /** If true, agent should not echo value back (passwords, tokens). Default: false */
153
- sensitive?: boolean;
154
- /**
155
- * Database column name. Defaults to key if not specified.
156
- *
157
- * WHY this exists:
158
- * - Form field names can be user-friendly ("Email Address")
159
- * - Database columns often have conventions ("email_address")
160
- * - Consuming plugins use this for mapping
161
- */
162
- dbbind?: string;
163
- /** Regex pattern for validation. Applied to string representation. */
164
- pattern?: string;
165
- /** Minimum value (for numbers) or minimum length (for strings) */
166
- min?: number;
167
- /** Maximum value (for numbers) or maximum length (for strings) */
168
- max?: number;
169
- /** Minimum string length (explicit, for when min is used for value) */
170
- minLength?: number;
171
- /** Maximum string length (explicit, for when max is used for value) */
172
- maxLength?: number;
173
- /** Allowed values (for string enums without select options) */
174
- enum?: string[];
175
- /** Options for 'select' type fields */
176
- options?: FormControlOption[];
177
- /** Configuration for 'file' type fields */
178
- file?: FormControlFileOptions;
179
- /** Default value if user doesn't provide one */
180
- defaultValue?: JsonValue;
181
- /** Conditional display/requirement based on another field */
182
- dependsOn?: FormControlDependency;
183
- /**
184
- * Role names that can see/fill this field.
185
- *
186
- * WHY field-level access:
187
- * - Some fields only admins should fill
188
- * - User shouldn't even know certain fields exist
189
- * - Example: "discount_code" only for "sales" role
190
- */
191
- roles?: string[];
192
- /**
193
- * Context description for LLM extraction.
194
- *
195
- * WHY this matters:
196
- * - LLM needs context to correctly interpret user messages
197
- * - "Enter your order" could be order number or food order
198
- * - Description clarifies intent
199
- */
200
- description?: string;
201
- /**
202
- * Custom prompt template when agent asks for this field.
203
- *
204
- * WHY custom prompts:
205
- * - "What's your email?" vs "Where should we send the confirmation?"
206
- * - Allows personality and context-appropriate phrasing
207
- */
208
- askPrompt?: string;
209
- /**
210
- * Keywords to help LLM extraction.
211
- *
212
- * WHY extraction hints:
213
- * - LLM might not know domain-specific patterns
214
- * - "wallet address" helps identify Base58 strings as Solana addresses
215
- */
216
- extractHints?: string[];
217
- /**
218
- * Confidence threshold for automatic acceptance. Default: 0.8.
219
- *
220
- * WHY configurable threshold:
221
- * - High-stakes fields (payment amount) need high confidence
222
- * - Low-stakes fields (nickname) can be more lenient
223
- */
224
- confirmThreshold?: number;
225
- /** Example value for "give me an example" request */
226
- example?: string;
227
- /** Hints for future GUI rendering */
228
- ui?: FormControlUI;
229
- /** Localized versions of label, description, askPrompt */
230
- i18n?: Record<string, FormControlI18n>;
231
- /**
232
- * Child fields for object/array types.
233
- *
234
- * WHY nested fields:
235
- * - Complex data like address (street, city, zip)
236
- * - Agent can collect as one logical unit
237
- * - Stored as nested object
238
- */
239
- fields?: FormControl[];
240
- /**
241
- * Arbitrary metadata for plugins.
242
- *
243
- * WHY meta field:
244
- * - Plugins may need custom data we didn't anticipate
245
- * - Avoids modifying core types
246
- * - Example: trading plugin adds 'slippage_tolerance'
247
- */
248
- meta?: Record<string, JsonValue>;
249
- }
250
- /**
251
- * UX options for the form.
252
- *
253
- * WHY form-level UX options:
254
- * - Different forms have different requirements
255
- * - Legal forms might disable undo
256
- * - Quick forms might disable autofill
257
- */
258
- export interface FormDefinitionUX {
259
- /** Allow "undo" to revert last change. Default: true */
260
- allowUndo?: boolean;
261
- /** Allow "skip" for optional fields. Default: true */
262
- allowSkip?: boolean;
263
- /** Maximum undo history size. Default: 5 */
264
- maxUndoSteps?: number;
265
- /** Show examples when user asks. Default: true */
266
- showExamples?: boolean;
267
- /** Show explanations when user asks. Default: true */
268
- showExplanations?: boolean;
269
- /** Allow autofill from previous submissions. Default: true */
270
- allowAutofill?: boolean;
271
- }
272
- /**
273
- * Smart TTL configuration.
274
- *
275
- * WHY effort-based TTL:
276
- * - User spending 2 hours on a form deserves weeks of retention
277
- * - User who started and abandoned deserves quick cleanup
278
- * - Respects user effort while managing storage
279
- */
280
- export interface FormDefinitionTTL {
281
- /** Minimum retention in days, even with no effort. Default: 14 */
282
- minDays?: number;
283
- /** Maximum retention in days, regardless of effort. Default: 90 */
284
- maxDays?: number;
285
- /**
286
- * Days added per minute of user effort. Default: 0.5.
287
- * Example: 10 min work = 5 extra days retention.
288
- */
289
- effortMultiplier?: number;
290
- }
291
- /**
292
- * Nudge configuration for stale sessions.
293
- *
294
- * WHY nudge system:
295
- * - Users forget about forms they started
296
- * - Gentle reminders increase completion rates
297
- * - But too many nudges are spammy
298
- */
299
- export interface FormDefinitionNudge {
300
- /** Enable nudge messages. Default: true */
301
- enabled?: boolean;
302
- /** Hours of inactivity before first nudge. Default: 48 */
303
- afterInactiveHours?: number;
304
- /** Maximum nudge messages to send. Default: 3 */
305
- maxNudges?: number;
306
- /** Custom nudge message template */
307
- message?: string;
308
- }
309
- /**
310
- * Hook configuration (task worker names).
311
- *
312
- * WHY hooks as task worker names:
313
- * - Consuming plugins define their own logic
314
- * - Hooks are async and can do anything
315
- * - Decouples form system from business logic
316
- */
317
- export interface FormDefinitionHooks {
318
- /** Called when session starts */
319
- onStart?: string;
320
- /** Called when any field changes */
321
- onFieldChange?: string;
322
- /** Called when all required fields are filled */
323
- onReady?: string;
324
- /** Called on successful submission */
325
- onSubmit?: string;
326
- /** Called when user cancels */
327
- onCancel?: string;
328
- /** Called when session expires */
329
- onExpire?: string;
330
- }
331
- /**
332
- * Localization for the form.
333
- */
334
- export interface FormDefinitionI18n {
335
- name?: string;
336
- description?: string;
337
- }
338
- /**
339
- * FormDefinition - The form container
340
- *
341
- * Defines a complete form including all its fields, lifecycle settings,
342
- * permissions, and hooks for consuming plugins.
343
- *
344
- * WHY separate from controls:
345
- * - Form-level settings affect all controls
346
- * - Hooks need form context
347
- * - Permissions apply to entire form
348
- */
349
- export interface FormDefinition {
350
- /** Unique identifier for this form definition */
351
- id: string;
352
- /** Human-readable name shown in UI and agent responses */
353
- name: string;
354
- /** Description of what this form collects */
355
- description?: string;
356
- /**
357
- * Schema version for migrations. Default: 1.
358
- *
359
- * WHY version:
360
- * - Forms evolve over time
361
- * - Old sessions might use old schema
362
- * - Version helps handle migrations
363
- */
364
- version?: number;
365
- /** Array of field definitions */
366
- controls: FormControl[];
367
- /**
368
- * Form status. Draft forms aren't startable.
369
- *
370
- * WHY status:
371
- * - Forms can be prepared but not yet active
372
- * - Deprecated forms shouldn't start new sessions
373
- * - Existing sessions on deprecated forms continue
374
- */
375
- status?: "draft" | "active" | "deprecated";
376
- /**
377
- * Roles that can start this form.
378
- *
379
- * WHY form-level roles:
380
- * - Some forms only for admins
381
- * - Prevents unauthorized data collection
382
- */
383
- roles?: string[];
384
- /**
385
- * Allow multiple submissions per user.
386
- *
387
- * WHY this flag:
388
- * - Registration forms: one per user
389
- * - Order forms: unlimited submissions
390
- * - Feedback forms: maybe one per session
391
- */
392
- allowMultiple?: boolean;
393
- ux?: FormDefinitionUX;
394
- ttl?: FormDefinitionTTL;
395
- nudge?: FormDefinitionNudge;
396
- hooks?: FormDefinitionHooks;
397
- /**
398
- * Enable debug logging for extraction.
399
- *
400
- * WHY debug flag:
401
- * - Extraction is LLM-based and can fail mysteriously
402
- * - Debug logs show LLM reasoning
403
- * - Off by default for performance
404
- */
405
- debug?: boolean;
406
- i18n?: Record<string, FormDefinitionI18n>;
407
- meta?: Record<string, JsonValue>;
408
- }
409
- /**
410
- * File attachment metadata.
411
- *
412
- * WHY separate from value:
413
- * - Files need special handling (storage, URLs)
414
- * - Metadata is safe to serialize, file content isn't
415
- * - URL might be temporary/signed
416
- */
417
- export interface FieldFile {
418
- /** Unique identifier for the file */
419
- id: string;
420
- /** Original filename */
421
- name: string;
422
- /** MIME type */
423
- mimeType: string;
424
- /** Size in bytes */
425
- size: number;
426
- /** URL to access the file */
427
- url: string;
428
- }
429
- /**
430
- * FieldState - Runtime state of a field
431
- *
432
- * Tracks the current value, validation status, confidence level,
433
- * and audit trail for a single field in an active form session.
434
- *
435
- * WHY this complexity:
436
- * - Agent extractions have confidence levels
437
- * - Users correct mistakes ("no, I meant...")
438
- * - Undo needs history
439
- * - Validation happens at extraction, not just submission
440
- */
441
- export interface FieldState {
442
- /**
443
- * Current status of this field.
444
- *
445
- * WHY multiple statuses:
446
- * - 'empty': Not yet provided
447
- * - 'filled': Value accepted
448
- * - 'uncertain': LLM not confident, needs confirmation
449
- * - 'invalid': Value failed validation
450
- * - 'skipped': User explicitly skipped optional field
451
- * - 'pending': External type activated, waiting for confirmation
452
- */
453
- status: "empty" | "filled" | "uncertain" | "invalid" | "skipped" | "pending";
454
- /** The current value (undefined if empty/skipped) */
455
- value?: JsonValue;
456
- /**
457
- * LLM confidence in extraction. 0-1.
458
- *
459
- * WHY track confidence:
460
- * - Low confidence triggers confirmation
461
- * - High confidence allows auto-acceptance
462
- * - Useful for debugging extraction issues
463
- */
464
- confidence?: number;
465
- /** Other possible interpretations of user message */
466
- alternatives?: JsonValue[];
467
- /** Validation error message if status is 'invalid' */
468
- error?: string;
469
- /** File metadata for file-type fields */
470
- files?: FieldFile[];
471
- /**
472
- * How this value was obtained.
473
- *
474
- * WHY track source:
475
- * - Autofilled values might need re-confirmation
476
- * - Corrections show user engagement
477
- * - Useful for analytics
478
- */
479
- source?: "extraction" | "autofill" | "default" | "manual" | "correction" | "external";
480
- /** ID of message that provided this value */
481
- messageId?: string;
482
- /** When the value was last updated */
483
- updatedAt?: number;
484
- /** When user confirmed an uncertain value */
485
- confirmedAt?: number;
486
- /**
487
- * Subfield states for composite control types.
488
- *
489
- * WHY subFields:
490
- * - Composite types (address, payment) have multiple parts
491
- * - Each part has its own state (filled, uncertain, etc.)
492
- * - Parent field is "filled" when all required subfields are filled
493
- *
494
- * Keyed by subcontrol key (e.g., "amount", "currency" for payment).
495
- */
496
- subFields?: Record<string, FieldState>;
497
- /**
498
- * State for external/async control types.
499
- *
500
- * WHY externalState:
501
- * - External types (payment, signature) have async lifecycle
502
- * - Need to track activation status, reference, instructions
503
- * - Separate from main status because field can be "pending" while
504
- * we wait for external confirmation
505
- *
506
- * @see ExternalFieldState for full interface
507
- */
508
- externalState?: {
509
- /** Current status of the external interaction */
510
- status: "pending" | "confirmed" | "failed" | "expired";
511
- /** Reference used to match external events */
512
- reference?: string;
513
- /** Instructions shown to user (cached from activation) */
514
- instructions?: string;
515
- /** Address shown to user (cached from activation) */
516
- address?: string;
517
- /** When the external process was activated */
518
- activatedAt?: number;
519
- /** When the external process was confirmed */
520
- confirmedAt?: number;
521
- /** Data from the external confirmation (txId, signature, etc.) */
522
- externalData?: Record<string, JsonValue>;
523
- };
524
- meta?: Record<string, JsonValue>;
525
- }
526
- /**
527
- * History entry for undo functionality.
528
- *
529
- * WHY track history:
530
- * - "Undo" is natural in conversation ("wait, go back")
531
- * - Need to know what to restore
532
- * - Limited history prevents memory bloat
533
- */
534
- export interface FieldHistoryEntry {
535
- /** Which field was changed */
536
- field: string;
537
- /** Previous value (to restore) */
538
- oldValue: JsonValue;
539
- /** New value (for audit) */
540
- newValue: JsonValue;
541
- /** When the change happened */
542
- timestamp: number;
543
- }
544
- /**
545
- * Effort tracking for smart TTL.
546
- *
547
- * WHY track effort:
548
- * - Forms abandoned quickly should expire quickly
549
- * - Forms worked on for hours deserve long retention
550
- * - Interaction count shows engagement even if time is short
551
- */
552
- export interface SessionEffort {
553
- /** Number of messages processed for this form */
554
- interactionCount: number;
555
- /** Total time from first to last interaction */
556
- timeSpentMs: number;
557
- /** When user first interacted with this form */
558
- firstInteractionAt: number;
559
- /** When user last interacted with this form */
560
- lastInteractionAt: number;
561
- }
562
- /**
563
- * FormSession - Active form state
564
- *
565
- * Represents an active form being filled by a user. This is the runtime
566
- * state that changes as the conversation progresses.
567
- *
568
- * WHY scoped to (entityId + roomId):
569
- * - Same user might fill different forms in different rooms
570
- * - Each room conversation has its own context
571
- * - User in Discord DM vs Telegram should have separate sessions
572
- *
573
- * WHY not just store values directly:
574
- * - Need to track status of each field
575
- * - Need confidence levels for confirmation
576
- * - Need history for undo
577
- * - Need metadata for analytics
578
- */
579
- export interface FormSession {
580
- /** Unique session ID */
581
- id: string;
582
- /** Reference to FormDefinition.id */
583
- formId: string;
584
- /** Form version at session start (for migration handling) */
585
- formVersion?: number;
586
- /** The user filling the form */
587
- entityId: UUID;
588
- /** The room where conversation is happening */
589
- roomId: UUID;
590
- /**
591
- * Session lifecycle status.
592
- *
593
- * WHY multiple statuses:
594
- * - 'active': Currently being filled
595
- * - 'ready': All required fields done, can submit
596
- * - 'submitted': Successfully submitted
597
- * - 'stashed': Saved for later
598
- * - 'cancelled': User abandoned
599
- * - 'expired': TTL exceeded
600
- */
601
- status: "active" | "ready" | "submitted" | "stashed" | "cancelled" | "expired";
602
- /** Current state of each field, keyed by control.key */
603
- fields: Record<string, FieldState>;
604
- /** Recent changes for undo functionality */
605
- history: FieldHistoryEntry[];
606
- /**
607
- * Parent session ID for subforms.
608
- *
609
- * WHY parent reference:
610
- * - Complex forms might have nested sections
611
- * - Subform completion triggers parent update
612
- * - Not yet implemented but structure is ready
613
- */
614
- parentSessionId?: string;
615
- /**
616
- * Arbitrary context from consuming plugin.
617
- *
618
- * WHY context field:
619
- * - Consuming plugin might pass order ID, user tier, etc.
620
- * - Affects how form behaves or what values are valid
621
- * - Stored with session for hook access
622
- */
623
- context?: Record<string, JsonValue>;
624
- /** User's locale for i18n */
625
- locale?: string;
626
- /** Last field agent asked about (for skip functionality) */
627
- lastAskedField?: string;
628
- /** Last message processed (for deduplication) */
629
- lastMessageId?: string;
630
- /** True if we asked "are you sure you want to cancel?" */
631
- cancelConfirmationAsked?: boolean;
632
- effort: SessionEffort;
633
- /** When this session expires (timestamp) */
634
- expiresAt: number;
635
- /** True if we already warned about expiration */
636
- expirationWarned?: boolean;
637
- /** Number of nudge messages sent */
638
- nudgeCount?: number;
639
- /** When we last sent a nudge */
640
- lastNudgeAt?: number;
641
- /** When session was created */
642
- createdAt: number;
643
- /** When session was last modified */
644
- updatedAt: number;
645
- /** When session was submitted (if status is 'submitted') */
646
- submittedAt?: number;
647
- meta?: Record<string, JsonValue>;
648
- }
649
- /**
650
- * FormSubmission - Completed form record
651
- *
652
- * The permanent record of a submitted form. This is what consuming
653
- * plugins use to create accounts, process orders, etc.
654
- *
655
- * WHY separate from session:
656
- * - Sessions are mutable, submissions are immutable
657
- * - Submissions don't need undo history, TTL, etc.
658
- * - Submissions are the "official record"
659
- */
660
- export interface FormSubmission {
661
- /** Unique submission ID */
662
- id: string;
663
- /** Which form definition this is for */
664
- formId: string;
665
- /** Form version at submission time */
666
- formVersion?: number;
667
- /** The session that produced this submission */
668
- sessionId: string;
669
- /** Who submitted */
670
- entityId: UUID;
671
- /** Field values keyed by control.key */
672
- values: Record<string, JsonValue>;
673
- /**
674
- * Same values but keyed by dbbind.
675
- *
676
- * WHY mappedValues:
677
- * - Convenience for consuming plugins
678
- * - No need to look up dbbind for each field
679
- * - Direct database insertion ready
680
- */
681
- mappedValues?: Record<string, JsonValue>;
682
- /** File attachments keyed by control.key */
683
- files?: Record<string, FieldFile[]>;
684
- /** When the form was submitted */
685
- submittedAt: number;
686
- meta?: Record<string, JsonValue>;
687
- }
688
- /**
689
- * ValidationResult - Standardized validation output
690
- *
691
- * WHY this structure:
692
- * - Consistent validation across all control types
693
- * - Error messages can be shown to users
694
- * - Simple boolean + optional error pattern
695
- */
696
- export interface ValidationResult {
697
- valid: boolean;
698
- error?: string;
699
- }
700
- /**
701
- * ActivationContext - Context passed to external control types
702
- *
703
- * WHY this exists:
704
- * - External types (payment, signature) need runtime access
705
- * - They need the session and control for context
706
- * - They need subfield values to know what to activate
707
- *
708
- * Example: Payment widget needs amount, currency, method from subcontrols
709
- * to generate the correct payment address and instructions.
710
- */
711
- export interface ActivationContext {
712
- /** Runtime for accessing services */
713
- runtime: import("@elizaos/core").IAgentRuntime;
714
- /** The current form session */
715
- session: FormSession;
716
- /** The control being activated */
717
- control: FormControl;
718
- /** Filled subcontrol values, keyed by subcontrol key */
719
- subValues: Record<string, JsonValue>;
720
- }
721
- /**
722
- * ExternalActivation - Result of activating an external control type
723
- *
724
- * WHY this structure:
725
- * - Instructions tell user what to do ("Send 0.5 SOL to xyz...")
726
- * - Reference uniquely identifies this activation for matching events
727
- * - Address is optional but common (payment address, signing target)
728
- * - ExpiresAt allows time-limited activations
729
- *
730
- * The reference is critical: when a blockchain event comes in,
731
- * we match it to the pending activation via this reference.
732
- */
733
- export interface ExternalActivation {
734
- /** Human-readable instructions for the user */
735
- instructions: string;
736
- /** Unique reference to match external events (e.g., memo, tx reference) */
737
- reference: string;
738
- /** Optional address (payment address, signing endpoint, etc.) */
739
- address?: string;
740
- /** When this activation expires (timestamp) */
741
- expiresAt?: number;
742
- /** Arbitrary metadata from the widget */
743
- meta?: Record<string, JsonValue>;
744
- }
745
- /**
746
- * ExternalFieldState - State tracking for external/async control types
747
- *
748
- * WHY this exists:
749
- * - External types have async lifecycles (pending → confirmed/failed)
750
- * - Need to store instructions for agent to communicate
751
- * - Need reference for matching events
752
- * - Need to track when things happened for debugging/TTL
753
- *
754
- * Stored within FieldState.externalState for fields using external types.
755
- */
756
- export interface ExternalFieldState {
757
- /** Current status of the external interaction */
758
- status: "pending" | "confirmed" | "failed" | "expired";
759
- /** Reference used to match external events */
760
- reference?: string;
761
- /** Instructions shown to user (cached from activation) */
762
- instructions?: string;
763
- /** Address shown to user (cached from activation) */
764
- address?: string;
765
- /** When the external process was activated */
766
- activatedAt?: number;
767
- /** When the external process was confirmed */
768
- confirmedAt?: number;
769
- /** Data from the external confirmation (txId, signature, etc.) */
770
- externalData?: Record<string, JsonValue>;
771
- }
772
- /**
773
- * ControlType - Unified widget/type registry entry
774
- *
775
- * This is the evolution of TypeHandler into a full widget system.
776
- * ControlType handles three patterns:
777
- *
778
- * 1. **Simple types** (text, number, email)
779
- * - Just validate/parse/format
780
- * - No subcontrols, no activation
781
- *
782
- * 2. **Composite types** (address, payment setup)
783
- * - Have subcontrols (getSubControls)
784
- * - Parent field is "filled" when all subcontrols filled
785
- * - No external activation
786
- *
787
- * 3. **External types** (payment, signature, file upload)
788
- * - May have subcontrols
789
- * - Have activate() for starting async process
790
- * - Confirmation comes from external event
791
- *
792
- * WHY unified interface:
793
- * - Plugins register one type of thing (ControlType)
794
- * - FormService treats all types uniformly
795
- * - Progressive complexity: simple types just use validate()
796
- *
797
- * WHY builtin flag:
798
- * - Protects standard types from accidental override
799
- * - Allows intentional override with allowOverride option
800
- * - Logs warning when override happens
801
- *
802
- * Example registrations:
803
- *
804
- * ```typescript
805
- * // Simple type
806
- * formService.registerControlType({
807
- * id: 'phone',
808
- * builtin: false,
809
- * validate: (v) => ({ valid: /^\+?[\d\s-]{10,}$/.test(String(v)) }),
810
- * extractionPrompt: 'a phone number with country code',
811
- * });
812
- *
813
- * // Composite type
814
- * formService.registerControlType({
815
- * id: 'address',
816
- * getSubControls: () => [
817
- * { key: 'street', type: 'text', label: 'Street', required: true },
818
- * { key: 'city', type: 'text', label: 'City', required: true },
819
- * { key: 'zip', type: 'text', label: 'ZIP', required: true },
820
- * ],
821
- * });
822
- *
823
- * // External type (payment)
824
- * formService.registerControlType({
825
- * id: 'payment',
826
- * getSubControls: () => [
827
- * { key: 'amount', type: 'number', label: 'Amount', required: true },
828
- * { key: 'currency', type: 'select', label: 'Currency', required: true },
829
- * ],
830
- * activate: async (ctx) => {
831
- * const paymentService = ctx.runtime.getService('PAYMENT');
832
- * return paymentService.createPendingPayment(ctx.subValues);
833
- * },
834
- * deactivate: async (ctx) => {
835
- * const ref = ctx.session.fields[ctx.control.key]?.externalState?.reference;
836
- * if (ref) await paymentService.cancelPending(ref);
837
- * },
838
- * });
839
- * ```
840
- */
841
- export interface ControlType {
842
- /** Unique identifier for this control type */
843
- id: string;
844
- /**
845
- * If true, this is a built-in type that should warn on override.
846
- * Built-in types: text, number, email, boolean, select, date, file
847
- */
848
- builtin?: boolean;
849
- /**
850
- * Validate a value for this type.
851
- * Called during extraction and before submission.
852
- */
853
- validate?: (value: JsonValue, control: FormControl) => ValidationResult;
854
- /**
855
- * Parse string input to the appropriate type.
856
- * Called when processing extracted values.
857
- */
858
- parse?: (value: string) => JsonValue;
859
- /**
860
- * Format value for display to user.
861
- * Called when showing field values in context.
862
- */
863
- format?: (value: JsonValue) => string;
864
- /**
865
- * Description for LLM extraction.
866
- * Helps the LLM understand what to look for in user messages.
867
- */
868
- extractionPrompt?: string;
869
- /**
870
- * Return subcontrols that must be filled before parent is complete.
871
- *
872
- * WHY runtime parameter:
873
- * - Subcontrols might depend on available services
874
- * - e.g., payment methods depend on what payment plugins are loaded
875
- *
876
- * Called by evaluator to understand the field structure.
877
- */
878
- getSubControls?: (control: FormControl, runtime: import("@elizaos/core").IAgentRuntime) => FormControl[];
879
- /**
880
- * Activate the external process.
881
- *
882
- * Called when all subcontrols are filled (or immediately for
883
- * external types without subcontrols).
884
- *
885
- * Returns activation info including instructions and reference.
886
- * The reference is used to match incoming external events.
887
- */
888
- activate?: (context: ActivationContext) => Promise<ExternalActivation>;
889
- /**
890
- * Deactivate/cancel a pending external process.
891
- *
892
- * Called when user cancels form or field is reset.
893
- * Should clean up any pending listeners/watchers.
894
- */
895
- deactivate?: (context: ActivationContext) => Promise<void>;
896
- }
897
- /**
898
- * Form widget event types
899
- *
900
- * WHY events:
901
- * - Widgets don't parse messages, evaluator does
902
- * - Widgets react to these standardized events
903
- * - Single source of truth for extraction
904
- * - Plugins can listen for analytics/logging
905
- *
906
- * The evaluator emits these as it processes messages.
907
- */
908
- export type FormWidgetEventType = "FORM_FIELD_EXTRACTED" | "FORM_SUBFIELD_UPDATED" | "FORM_SUBCONTROLS_FILLED" | "FORM_EXTERNAL_ACTIVATED" | "FORM_FIELD_CONFIRMED" | "FORM_FIELD_CANCELLED";
909
- /**
910
- * Payload for FORM_FIELD_EXTRACTED event
911
- */
912
- export interface FormFieldExtractedEvent {
913
- type: "FORM_FIELD_EXTRACTED";
914
- sessionId: string;
915
- field: string;
916
- value: JsonValue;
917
- confidence: number;
918
- }
919
- /**
920
- * Payload for FORM_SUBFIELD_UPDATED event
921
- */
922
- export interface FormSubfieldUpdatedEvent {
923
- type: "FORM_SUBFIELD_UPDATED";
924
- sessionId: string;
925
- parentField: string;
926
- subField: string;
927
- value: JsonValue;
928
- confidence: number;
929
- }
930
- /**
931
- * Payload for FORM_SUBCONTROLS_FILLED event
932
- */
933
- export interface FormSubcontrolsFilledEvent {
934
- type: "FORM_SUBCONTROLS_FILLED";
935
- sessionId: string;
936
- field: string;
937
- subValues: Record<string, JsonValue>;
938
- }
939
- /**
940
- * Payload for FORM_EXTERNAL_ACTIVATED event
941
- */
942
- export interface FormExternalActivatedEvent {
943
- type: "FORM_EXTERNAL_ACTIVATED";
944
- sessionId: string;
945
- field: string;
946
- activation: ExternalActivation;
947
- }
948
- /**
949
- * Payload for FORM_FIELD_CONFIRMED event
950
- */
951
- export interface FormFieldConfirmedEvent {
952
- type: "FORM_FIELD_CONFIRMED";
953
- sessionId: string;
954
- field: string;
955
- value: JsonValue;
956
- externalData?: Record<string, JsonValue>;
957
- }
958
- /**
959
- * Payload for FORM_FIELD_CANCELLED event
960
- */
961
- export interface FormFieldCancelledEvent {
962
- type: "FORM_FIELD_CANCELLED";
963
- sessionId: string;
964
- field: string;
965
- reason: string;
966
- }
967
- /**
968
- * Union of all form widget events
969
- */
970
- export type FormWidgetEvent = FormFieldExtractedEvent | FormSubfieldUpdatedEvent | FormSubcontrolsFilledEvent | FormExternalActivatedEvent | FormFieldConfirmedEvent | FormFieldCancelledEvent;
971
- /**
972
- * Filled field summary for context.
973
- */
974
- export interface FilledFieldSummary {
975
- key: string;
976
- label: string;
977
- /** Formatted value safe to show user (respects sensitive flag) */
978
- displayValue: string;
979
- }
980
- /**
981
- * Missing field summary for context.
982
- */
983
- export interface MissingFieldSummary {
984
- key: string;
985
- label: string;
986
- description?: string;
987
- /** How agent should ask for this field */
988
- askPrompt?: string;
989
- }
990
- /**
991
- * Uncertain field summary for confirmation.
992
- */
993
- export interface UncertainFieldSummary {
994
- key: string;
995
- label: string;
996
- /** The uncertain value */
997
- value: JsonValue;
998
- /** How confident the LLM was */
999
- confidence: number;
1000
- }
1001
- /**
1002
- * FormContextState - Provider output for agent
1003
- *
1004
- * The context injected into the agent's state, giving it awareness
1005
- * of the current form progress and what to do next.
1006
- *
1007
- * WHY this structure:
1008
- * - Agent needs to know what's filled (for progress updates)
1009
- * - Agent needs to know what's missing (to ask)
1010
- * - Agent needs to know what's uncertain (to confirm)
1011
- * - Agent needs suggested actions (what to do next)
1012
- */
1013
- /**
1014
- * Summary of a pending external field for agent context.
1015
- */
1016
- export interface PendingExternalFieldSummary {
1017
- /** Field key */
1018
- key: string;
1019
- /** Field label for display */
1020
- label: string;
1021
- /** Instructions for the user (e.g., "Send 0.5 SOL to xyz...") */
1022
- instructions: string;
1023
- /** Reference for matching (may be shown to user) */
1024
- reference: string;
1025
- /** When the external process was activated */
1026
- activatedAt: number;
1027
- /** Optional address (payment address, etc.) */
1028
- address?: string;
1029
- }
1030
- export interface FormContextState {
1031
- /** True if there's an active form in this room */
1032
- hasActiveForm: boolean;
1033
- /** Current form ID */
1034
- formId?: string;
1035
- /** Current form name */
1036
- formName?: string;
1037
- /** Completion percentage (0-100) */
1038
- progress: number;
1039
- /** Fields that have been filled */
1040
- filledFields: FilledFieldSummary[];
1041
- /** Required fields still needed */
1042
- missingRequired: MissingFieldSummary[];
1043
- /** Fields needing user confirmation */
1044
- uncertainFields: UncertainFieldSummary[];
1045
- /** Next field to ask about */
1046
- nextField: FormControl | null;
1047
- /** Current session status */
1048
- status?: FormSession["status"];
1049
- /** Number of stashed forms (for "you have saved forms" prompt) */
1050
- stashedCount?: number;
1051
- /** True if we asked "are you sure you want to cancel?" */
1052
- pendingCancelConfirmation?: boolean;
1053
- /**
1054
- * External fields waiting for confirmation.
1055
- *
1056
- * WHY this exists:
1057
- * - Agent needs to remind user about pending payments/signatures
1058
- * - Shows instructions and reference for user to act on
1059
- * - Allows agent to check "still waiting for your payment..."
1060
- */
1061
- pendingExternalFields: PendingExternalFieldSummary[];
1062
- }
1063
- /**
1064
- * All supported user intents.
1065
- *
1066
- * WHY explicit intent types:
1067
- * - Type safety for intent handling
1068
- * - Clear documentation of what's supported
1069
- * - Easy to add new intents
1070
- */
1071
- export type FormIntent = "fill_form" | "submit" | "stash" | "restore" | "cancel" | "undo" | "skip" | "explain" | "example" | "progress" | "autofill" | "other";
1072
- /**
1073
- * Extraction result for a single field.
1074
- *
1075
- * WHY this structure:
1076
- * - Need value and confidence together
1077
- * - Need to know if this is correcting a previous value
1078
- * - Need reasoning for debugging
1079
- */
1080
- export interface ExtractionResult {
1081
- /** Which field this is for */
1082
- field: string;
1083
- /** Extracted value */
1084
- value: JsonValue;
1085
- /** LLM confidence (0-1) */
1086
- confidence: number;
1087
- /** LLM reasoning (for debug mode) */
1088
- reasoning?: string;
1089
- /** Other possible values if uncertain */
1090
- alternatives?: JsonValue[];
1091
- /** True if user is correcting previous value */
1092
- isCorrection?: boolean;
1093
- }
1094
- /**
1095
- * Combined intent and extraction result.
1096
- *
1097
- * WHY combined:
1098
- * - Single LLM call extracts both intent and values
1099
- * - Reduces latency
1100
- * - Context helps with both
1101
- */
1102
- export interface IntentResult {
1103
- /** What the user wants to do */
1104
- intent: FormIntent;
1105
- /** Extracted field values (for fill_form intent) */
1106
- extractions: ExtractionResult[];
1107
- /** Target form ID for restore if multiple stashed */
1108
- targetFormId?: string;
1109
- }
1110
- /**
1111
- * Default values for FormControl.
1112
- *
1113
- * WHY explicit defaults:
1114
- * - Clear documentation of behavior
1115
- * - Used by applyControlDefaults()
1116
- * - Can be overridden per-control
1117
- */
1118
- export declare const FORM_CONTROL_DEFAULTS: {
1119
- readonly type: "text";
1120
- readonly required: false;
1121
- readonly confirmThreshold: 0.8;
1122
- };
1123
- /**
1124
- * Default values for FormDefinition.
1125
- *
1126
- * WHY these specific defaults:
1127
- * - 14 days min TTL: Long enough for user to return
1128
- * - 90 days max TTL: Not forever, but generous
1129
- * - 0.5 effort multiplier: 10 min work = 5 extra days
1130
- * - 48h nudge: Not too aggressive
1131
- * - 3 max nudges: Helpful but not spammy
1132
- */
1133
- export declare const FORM_DEFINITION_DEFAULTS: {
1134
- readonly version: 1;
1135
- readonly status: "active";
1136
- readonly ux: {
1137
- readonly allowUndo: true;
1138
- readonly allowSkip: true;
1139
- readonly maxUndoSteps: 5;
1140
- readonly showExamples: true;
1141
- readonly showExplanations: true;
1142
- readonly allowAutofill: true;
1143
- };
1144
- readonly ttl: {
1145
- readonly minDays: 14;
1146
- readonly maxDays: 90;
1147
- readonly effortMultiplier: 0.5;
1148
- };
1149
- readonly nudge: {
1150
- readonly enabled: true;
1151
- readonly afterInactiveHours: 48;
1152
- readonly maxNudges: 3;
1153
- };
1154
- readonly debug: false;
1155
- };
1156
- /**
1157
- * Component type prefix for form sessions.
1158
- *
1159
- * WHY component-based storage:
1160
- * - Components are ElizaOS's entity data storage
1161
- * - Scoped to entity, can include room in type
1162
- * - Automatic CRUD via runtime
1163
- */
1164
- export declare const FORM_SESSION_COMPONENT = "form_session";
1165
- /**
1166
- * Component type prefix for form submissions.
1167
- */
1168
- export declare const FORM_SUBMISSION_COMPONENT = "form_submission";
1169
- /**
1170
- * Component type prefix for autofill data.
1171
- */
1172
- export declare const FORM_AUTOFILL_COMPONENT = "form_autofill";
1173
- /**
1174
- * Autofill data stored per user per form.
1175
- *
1176
- * WHY store autofill:
1177
- * - Users filling repeat forms want saved values
1178
- * - Stored per user per form (not global)
1179
- * - Updated on each submission
1180
- */
1181
- export interface FormAutofillData {
1182
- formId: string;
1183
- values: Record<string, JsonValue>;
1184
- updatedAt: number;
1185
- }
1186
- //# sourceMappingURL=types.d.ts.map