@elizaos/plugin-form 2.0.0-alpha.9 → 2.0.0-beta.1
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/auto-enable.ts +24 -0
- package/dist/actions/restore.d.ts +25 -0
- package/dist/actions/restore.d.ts.map +1 -0
- package/dist/actions/restore.js +176 -0
- package/dist/actions/restore.js.map +1 -0
- package/dist/builder.d.ts +320 -0
- package/dist/builder.d.ts.map +1 -0
- package/dist/builder.js +458 -0
- package/dist/builder.js.map +1 -0
- package/dist/builtins.d.ts +128 -0
- package/dist/builtins.d.ts.map +1 -0
- package/dist/builtins.js +233 -0
- package/dist/builtins.js.map +1 -0
- package/dist/defaults.d.ts +95 -0
- package/dist/defaults.d.ts.map +1 -0
- package/dist/defaults.js +79 -0
- package/dist/defaults.js.map +1 -0
- package/dist/evaluators/extractor.d.ts +28 -0
- package/dist/evaluators/extractor.d.ts.map +1 -0
- package/dist/evaluators/extractor.js +247 -0
- package/dist/evaluators/extractor.js.map +1 -0
- package/dist/extraction.d.ts +55 -0
- package/dist/extraction.d.ts.map +1 -0
- package/dist/extraction.js +331 -0
- package/dist/extraction.js.map +1 -0
- package/dist/index.d.ts +31 -2
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +119 -3284
- package/dist/index.js.map +1 -30
- package/dist/providers/context.d.ts +56 -0
- package/dist/providers/context.d.ts.map +1 -0
- package/dist/providers/context.js +206 -0
- package/dist/providers/context.js.map +1 -0
- package/dist/service.d.ts +402 -0
- package/dist/service.d.ts.map +1 -0
- package/dist/service.js +1158 -0
- package/dist/service.js.map +1 -0
- package/dist/storage.d.ts +228 -0
- package/dist/storage.d.ts.map +1 -0
- package/dist/storage.js +218 -0
- package/dist/storage.js.map +1 -0
- package/dist/template.d.ts +10 -0
- package/dist/template.d.ts.map +1 -0
- package/dist/template.js +60 -0
- package/dist/template.js.map +1 -0
- package/dist/ttl.d.ts +144 -0
- package/dist/ttl.d.ts.map +1 -0
- package/dist/ttl.js +85 -0
- package/dist/ttl.js.map +1 -0
- package/dist/types.d.ts +1213 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +39 -0
- package/dist/types.js.map +1 -0
- package/dist/validation.d.ts +156 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/validation.js +289 -0
- package/dist/validation.js.map +1 -0
- package/package.json +39 -42
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module providers/context
|
|
3
|
+
* @description Form context provider for agent awareness
|
|
4
|
+
*
|
|
5
|
+
* ## Purpose
|
|
6
|
+
*
|
|
7
|
+
* This provider injects form state into the agent's context BEFORE
|
|
8
|
+
* the agent generates a response. This allows the agent to:
|
|
9
|
+
*
|
|
10
|
+
* 1. Know if a form is active
|
|
11
|
+
* 2. Know what required/optional fields we have vs don't have
|
|
12
|
+
* 3. Know what needs confirmation (low-confidence extractions)
|
|
13
|
+
* 4. Know what external actions are pending (payments, signatures, etc.)
|
|
14
|
+
* 5. Get a single, coherent instruction (nudge for required, confirm, or submit)
|
|
15
|
+
*
|
|
16
|
+
* ## Output layout
|
|
17
|
+
*
|
|
18
|
+
* The text output uses a required/optional × have/don't-have layout so the
|
|
19
|
+
* agent sees the full picture at a glance and can ask for one or several
|
|
20
|
+
* missing fields in a single message (the form extracts and saves each).
|
|
21
|
+
*
|
|
22
|
+
* ## Context Output
|
|
23
|
+
*
|
|
24
|
+
* - `data`: Full FormContextState (programmatic access; e.g. restore action uses nextField)
|
|
25
|
+
* - `values`: String values for template substitution (formContext, formProgress, etc.)
|
|
26
|
+
* - `text`: Human-readable summary injected into the agent prompt
|
|
27
|
+
*
|
|
28
|
+
* ## How It Works
|
|
29
|
+
*
|
|
30
|
+
* ```
|
|
31
|
+
* User Message → Provider Runs → Agent Gets Context → Agent Responds
|
|
32
|
+
* ↓
|
|
33
|
+
* FormContextState
|
|
34
|
+
* ↓
|
|
35
|
+
* - hasActiveForm, progress
|
|
36
|
+
* - required/optional × have/don't have
|
|
37
|
+
* - uncertainFields, pendingExternalFields
|
|
38
|
+
* - single Instruction line
|
|
39
|
+
* ```
|
|
40
|
+
*
|
|
41
|
+
* ## Stashed Forms
|
|
42
|
+
*
|
|
43
|
+
* If the user has stashed forms, the provider appends a reminder so the
|
|
44
|
+
* agent can tell the user they have unfinished work and can say "resume".
|
|
45
|
+
*/
|
|
46
|
+
import type { Provider } from "@elizaos/core";
|
|
47
|
+
/**
|
|
48
|
+
* Form Context Provider
|
|
49
|
+
*
|
|
50
|
+
* Injects the current form state into the agent's context,
|
|
51
|
+
* allowing the agent to respond naturally about form progress
|
|
52
|
+
* and nudge for missing fields (one or several at once).
|
|
53
|
+
*/
|
|
54
|
+
export declare const formContextProvider: Provider;
|
|
55
|
+
export default formContextProvider;
|
|
56
|
+
//# sourceMappingURL=context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/providers/context.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AAEH,OAAO,KAAK,EAIV,QAAQ,EAIT,MAAM,eAAe,CAAC;AAiBvB;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB,EAAE,QAuPjC,CAAC;AAEF,eAAe,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import { logger } from "@elizaos/core";
|
|
2
|
+
import {
|
|
3
|
+
buildTemplateValues,
|
|
4
|
+
renderTemplate,
|
|
5
|
+
resolveControlTemplates
|
|
6
|
+
} from "../template";
|
|
7
|
+
function compactJson(value) {
|
|
8
|
+
return JSON.stringify(value, null, 2);
|
|
9
|
+
}
|
|
10
|
+
const MAX_CONTEXT_FIELDS = 20;
|
|
11
|
+
const MAX_STASHED_FOR_CONTEXT = 10;
|
|
12
|
+
const formContextProvider = {
|
|
13
|
+
name: "FORM_CONTEXT",
|
|
14
|
+
description: "Provides context about active form sessions",
|
|
15
|
+
descriptionCompressed: "Active form session context.",
|
|
16
|
+
contexts: ["automation", "knowledge"],
|
|
17
|
+
contextGate: { anyOf: ["automation", "knowledge"] },
|
|
18
|
+
cacheStable: false,
|
|
19
|
+
cacheScope: "turn",
|
|
20
|
+
/**
|
|
21
|
+
* Get form context for the current message.
|
|
22
|
+
*
|
|
23
|
+
* @param runtime - Agent runtime for service access
|
|
24
|
+
* @param message - The user message being processed
|
|
25
|
+
* @param _state - Current agent state (unused)
|
|
26
|
+
* @returns Provider result with form context (data, values, text)
|
|
27
|
+
*/
|
|
28
|
+
get: async (runtime, message, _state) => {
|
|
29
|
+
try {
|
|
30
|
+
const formService = runtime.getService("FORM");
|
|
31
|
+
if (!formService) {
|
|
32
|
+
return {
|
|
33
|
+
data: { hasActiveForm: false },
|
|
34
|
+
values: { formContext: "" },
|
|
35
|
+
text: ""
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
const entityId = message.entityId;
|
|
39
|
+
const roomId = message.roomId;
|
|
40
|
+
if (!entityId || !roomId) {
|
|
41
|
+
return {
|
|
42
|
+
data: { hasActiveForm: false },
|
|
43
|
+
values: { formContext: "" },
|
|
44
|
+
text: ""
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
const session = await formService.getActiveSession(entityId, roomId);
|
|
48
|
+
const stashed = await formService.getStashedSessions(entityId);
|
|
49
|
+
if (!session && stashed.length === 0) {
|
|
50
|
+
return {
|
|
51
|
+
data: { hasActiveForm: false, stashedCount: 0 },
|
|
52
|
+
values: { formContext: "" },
|
|
53
|
+
text: ""
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
let contextText = "";
|
|
57
|
+
let contextState;
|
|
58
|
+
let stashedRows = [];
|
|
59
|
+
if (session) {
|
|
60
|
+
contextState = formService.getSessionContext(session);
|
|
61
|
+
const form = formService.getForm(session.formId);
|
|
62
|
+
const templateValues = buildTemplateValues(session);
|
|
63
|
+
const resolve = (v) => renderTemplate(v, templateValues);
|
|
64
|
+
contextState = {
|
|
65
|
+
...contextState,
|
|
66
|
+
filledFields: contextState.filledFields.map((f) => ({
|
|
67
|
+
...f,
|
|
68
|
+
label: resolve(f.label) ?? f.label
|
|
69
|
+
})),
|
|
70
|
+
missingRequired: contextState.missingRequired.map((f) => ({
|
|
71
|
+
...f,
|
|
72
|
+
label: resolve(f.label) ?? f.label,
|
|
73
|
+
description: resolve(f.description),
|
|
74
|
+
askPrompt: resolve(f.askPrompt)
|
|
75
|
+
})),
|
|
76
|
+
uncertainFields: contextState.uncertainFields.map((f) => ({
|
|
77
|
+
...f,
|
|
78
|
+
label: resolve(f.label) ?? f.label
|
|
79
|
+
})),
|
|
80
|
+
nextField: contextState.nextField ? resolveControlTemplates(contextState.nextField, templateValues) : null
|
|
81
|
+
};
|
|
82
|
+
const controls = form?.controls ?? [];
|
|
83
|
+
const filledKeys = new Set(contextState.filledFields.map((f) => f.key));
|
|
84
|
+
const controlByKey = new Map(controls.map((c) => [c.key, c]));
|
|
85
|
+
const requiredFilled = contextState.filledFields.filter(
|
|
86
|
+
(f) => controlByKey.get(f.key)?.required
|
|
87
|
+
);
|
|
88
|
+
const optionalFilled = contextState.filledFields.filter(
|
|
89
|
+
(f) => !controlByKey.get(f.key)?.required
|
|
90
|
+
);
|
|
91
|
+
const optionalMissing = controls.filter((c) => !c.hidden && !c.required && !filledKeys.has(c.key)).map((c) => resolveControlTemplates(c, templateValues));
|
|
92
|
+
let instruction = "";
|
|
93
|
+
if (contextState.pendingExternalFields.length > 0) {
|
|
94
|
+
const p = contextState.pendingExternalFields[0];
|
|
95
|
+
instruction = `Waiting for external action. Remind user: "${p.instructions}"`;
|
|
96
|
+
} else if (contextState.pendingCancelConfirmation) {
|
|
97
|
+
instruction = "User is trying to cancel. Confirm they really want to lose progress.";
|
|
98
|
+
} else if (contextState.uncertainFields.length > 0) {
|
|
99
|
+
const u = contextState.uncertainFields[0];
|
|
100
|
+
instruction = `Ask user to confirm "${u.label}" = "${u.value}".`;
|
|
101
|
+
} else if (contextState.missingRequired.length > 0) {
|
|
102
|
+
instruction = "Nudge the user into helping complete required fields. The user can provide one or several answers in a single message.";
|
|
103
|
+
} else if (contextState.status === "ready") {
|
|
104
|
+
instruction = "All required fields collected. Nudge user to submit.";
|
|
105
|
+
} else if (optionalMissing.length > 0) {
|
|
106
|
+
instruction = "Required fields are done. Optionally nudge for remaining optional fields, or nudge to submit.";
|
|
107
|
+
}
|
|
108
|
+
contextText = `form_context_json:
|
|
109
|
+
${compactJson({
|
|
110
|
+
active: true,
|
|
111
|
+
form_id: session.formId,
|
|
112
|
+
form_name: form?.name || session.formId,
|
|
113
|
+
progress: contextState.progress,
|
|
114
|
+
status: contextState.status,
|
|
115
|
+
required_missing: contextState.missingRequired.slice(
|
|
116
|
+
0,
|
|
117
|
+
MAX_CONTEXT_FIELDS
|
|
118
|
+
),
|
|
119
|
+
required_filled: requiredFilled.map((field) => ({
|
|
120
|
+
...field,
|
|
121
|
+
value: field.displayValue
|
|
122
|
+
})).slice(0, MAX_CONTEXT_FIELDS),
|
|
123
|
+
optional_missing: optionalMissing.slice(0, MAX_CONTEXT_FIELDS),
|
|
124
|
+
optional_filled: optionalFilled.map((field) => ({
|
|
125
|
+
...field,
|
|
126
|
+
value: field.displayValue
|
|
127
|
+
})).slice(0, MAX_CONTEXT_FIELDS),
|
|
128
|
+
uncertain_fields: contextState.uncertainFields.slice(0, MAX_CONTEXT_FIELDS).map((field) => ({
|
|
129
|
+
...field,
|
|
130
|
+
confidence: Math.round(field.confidence * 100) / 100
|
|
131
|
+
})),
|
|
132
|
+
pending_external_fields: contextState.pendingExternalFields.map(
|
|
133
|
+
(field) => ({
|
|
134
|
+
...field,
|
|
135
|
+
age_minutes: Math.max(
|
|
136
|
+
0,
|
|
137
|
+
Math.floor((Date.now() - field.activatedAt) / 6e4)
|
|
138
|
+
)
|
|
139
|
+
})
|
|
140
|
+
).slice(0, MAX_CONTEXT_FIELDS),
|
|
141
|
+
instruction
|
|
142
|
+
})}`;
|
|
143
|
+
} else {
|
|
144
|
+
contextState = {
|
|
145
|
+
hasActiveForm: false,
|
|
146
|
+
progress: 0,
|
|
147
|
+
filledFields: [],
|
|
148
|
+
missingRequired: [],
|
|
149
|
+
uncertainFields: [],
|
|
150
|
+
nextField: null,
|
|
151
|
+
stashedCount: stashed.length,
|
|
152
|
+
pendingExternalFields: []
|
|
153
|
+
};
|
|
154
|
+
contextText = `form_context_json:
|
|
155
|
+
${compactJson({
|
|
156
|
+
active: false,
|
|
157
|
+
progress: 0,
|
|
158
|
+
stashed_count: stashed.length
|
|
159
|
+
})}`;
|
|
160
|
+
}
|
|
161
|
+
if (stashed.length > 0) {
|
|
162
|
+
stashedRows = stashed.slice(0, MAX_STASHED_FOR_CONTEXT).map((s) => {
|
|
163
|
+
const f = formService.getForm(s.formId);
|
|
164
|
+
const ctx = formService.getSessionContext(s);
|
|
165
|
+
return {
|
|
166
|
+
form_id: s.formId,
|
|
167
|
+
form_name: f?.name || s.formId,
|
|
168
|
+
progress: ctx.progress
|
|
169
|
+
};
|
|
170
|
+
});
|
|
171
|
+
contextText += `
|
|
172
|
+
stashed_forms_json:
|
|
173
|
+
${compactJson(stashedRows)}`;
|
|
174
|
+
contextText += "\nstashed_instruction: User can say resume to restore one.";
|
|
175
|
+
}
|
|
176
|
+
return {
|
|
177
|
+
// Full context object for programmatic access
|
|
178
|
+
// WHY: Restore action and others read data.nextField, data.filledFields, etc.
|
|
179
|
+
data: JSON.parse(JSON.stringify(contextState)),
|
|
180
|
+
// String values for template substitution (e.g. in prompts: formContext, formProgress, formStatus)
|
|
181
|
+
values: {
|
|
182
|
+
formContext: contextText,
|
|
183
|
+
hasActiveForm: String(contextState.hasActiveForm),
|
|
184
|
+
formProgress: String(contextState.progress),
|
|
185
|
+
formStatus: contextState.status || "",
|
|
186
|
+
stashedCount: String(stashed.length)
|
|
187
|
+
},
|
|
188
|
+
// Human-readable text for agent (injected into prompt)
|
|
189
|
+
text: contextText
|
|
190
|
+
};
|
|
191
|
+
} catch (error) {
|
|
192
|
+
logger.error("[FormContextProvider] Error:", String(error));
|
|
193
|
+
return {
|
|
194
|
+
data: { hasActiveForm: false, error: true },
|
|
195
|
+
values: { formContext: 'form_context_json:\n{"error":true}' },
|
|
196
|
+
text: 'form_context_json:\n{"error":true}'
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
var context_default = formContextProvider;
|
|
202
|
+
export {
|
|
203
|
+
context_default as default,
|
|
204
|
+
formContextProvider
|
|
205
|
+
};
|
|
206
|
+
//# sourceMappingURL=context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/providers/context.ts"],"sourcesContent":["/**\n * @module providers/context\n * @description Form context provider for agent awareness\n *\n * ## Purpose\n *\n * This provider injects form state into the agent's context BEFORE\n * the agent generates a response. This allows the agent to:\n *\n * 1. Know if a form is active\n * 2. Know what required/optional fields we have vs don't have\n * 3. Know what needs confirmation (low-confidence extractions)\n * 4. Know what external actions are pending (payments, signatures, etc.)\n * 5. Get a single, coherent instruction (nudge for required, confirm, or submit)\n *\n * ## Output layout\n *\n * The text output uses a required/optional × have/don't-have layout so the\n * agent sees the full picture at a glance and can ask for one or several\n * missing fields in a single message (the form extracts and saves each).\n *\n * ## Context Output\n *\n * - `data`: Full FormContextState (programmatic access; e.g. restore action uses nextField)\n * - `values`: String values for template substitution (formContext, formProgress, etc.)\n * - `text`: Human-readable summary injected into the agent prompt\n *\n * ## How It Works\n *\n * ```\n * User Message → Provider Runs → Agent Gets Context → Agent Responds\n * ↓\n * FormContextState\n * ↓\n * - hasActiveForm, progress\n * - required/optional × have/don't have\n * - uncertainFields, pendingExternalFields\n * - single Instruction line\n * ```\n *\n * ## Stashed Forms\n *\n * If the user has stashed forms, the provider appends a reminder so the\n * agent can tell the user they have unfinished work and can say \"resume\".\n */\n\nimport type {\n IAgentRuntime,\n JsonValue,\n Memory,\n Provider,\n ProviderResult,\n State,\n UUID,\n} from \"@elizaos/core\";\nimport { logger } from \"@elizaos/core\";\nimport type { FormService } from \"../service\";\nimport {\n buildTemplateValues,\n renderTemplate,\n resolveControlTemplates,\n} from \"../template\";\nimport type { FormContextState } from \"../types\";\n\nfunction compactJson(value: unknown): string {\n return JSON.stringify(value, null, 2);\n}\n\nconst MAX_CONTEXT_FIELDS = 20;\nconst MAX_STASHED_FOR_CONTEXT = 10;\n\n/**\n * Form Context Provider\n *\n * Injects the current form state into the agent's context,\n * allowing the agent to respond naturally about form progress\n * and nudge for missing fields (one or several at once).\n */\nexport const formContextProvider: Provider = {\n name: \"FORM_CONTEXT\",\n description: \"Provides context about active form sessions\",\n descriptionCompressed: \"Active form session context.\",\n contexts: [\"automation\", \"knowledge\"],\n contextGate: { anyOf: [\"automation\", \"knowledge\"] },\n cacheStable: false,\n cacheScope: \"turn\",\n\n /**\n * Get form context for the current message.\n *\n * @param runtime - Agent runtime for service access\n * @param message - The user message being processed\n * @param _state - Current agent state (unused)\n * @returns Provider result with form context (data, values, text)\n */\n get: async (\n runtime: IAgentRuntime,\n message: Memory,\n _state: State,\n ): Promise<ProviderResult> => {\n try {\n // Get form service\n // WHY type cast: Runtime returns unknown, we know it's FormService\n const formService = runtime.getService(\"FORM\") as FormService;\n if (!formService) {\n // WHY early return: No form plugin registered or FORM service not available\n return {\n data: { hasActiveForm: false },\n values: { formContext: \"\" },\n text: \"\",\n };\n }\n\n // Get entity and room IDs\n // WHY UUID cast: Memory has these as unknown, we need proper typing for storage lookups\n const entityId = message.entityId as UUID;\n const roomId = message.roomId as UUID;\n if (!entityId || !roomId) {\n // WHY early return: Cannot look up session without identity and room\n return {\n data: { hasActiveForm: false },\n values: { formContext: \"\" },\n text: \"\",\n };\n }\n\n // Get active session for this room\n const session = await formService.getActiveSession(entityId, roomId);\n // Get stashed sessions (for \"you have saved forms\" prompt)\n const stashed = await formService.getStashedSessions(entityId);\n\n // If no active session and no stashed, nothing to provide\n if (!session && stashed.length === 0) {\n return {\n data: { hasActiveForm: false, stashedCount: 0 },\n values: { formContext: \"\" },\n text: \"\",\n };\n }\n\n let contextText = \"\";\n let contextState: FormContextState;\n let stashedRows: Array<Record<string, unknown>> = [];\n\n if (session) {\n // Build context for active session\n // Get session context from service\n // WHY: Service computes filledFields, missingRequired, uncertainFields, nextField from session + form definition\n contextState = formService.getSessionContext(session);\n const form = formService.getForm(session.formId);\n // Build template values from session (for {{placeholders}} in labels, askPrompt, etc.)\n const templateValues = buildTemplateValues(session);\n // WHY resolve: Form definitions may use {{variable}} in label, description, askPrompt; renderTemplate substitutes from session\n const resolve = (v?: string): string | undefined =>\n renderTemplate(v, templateValues);\n\n // Apply template resolution to all user-facing strings\n // WHY: Agent and user see resolved labels (e.g. \"{{discoveryQuestion1Text}}\" → actual question text)\n contextState = {\n ...contextState,\n filledFields: contextState.filledFields.map((f) => ({\n ...f,\n label: resolve(f.label) ?? f.label,\n })),\n missingRequired: contextState.missingRequired.map((f) => ({\n ...f,\n label: resolve(f.label) ?? f.label,\n description: resolve(f.description),\n askPrompt: resolve(f.askPrompt),\n })),\n uncertainFields: contextState.uncertainFields.map((f) => ({\n ...f,\n label: resolve(f.label) ?? f.label,\n })),\n nextField: contextState.nextField\n ? resolveControlTemplates(contextState.nextField, templateValues)\n : null,\n };\n // WHY nextField in data: Restore action reads contextState.nextField for \"Let's continue with X\"\n\n // Partition controls into required/optional × filled/missing\n // WHY four buckets: Agent needs full picture at a glance; can nudge for required and optionally for optional; can ask for one or bundle several\n const controls = form?.controls ?? [];\n const filledKeys = new Set(contextState.filledFields.map((f) => f.key));\n const controlByKey = new Map(controls.map((c) => [c.key, c]));\n\n const requiredFilled = contextState.filledFields.filter(\n (f) => controlByKey.get(f.key)?.required,\n );\n const optionalFilled = contextState.filledFields.filter(\n (f) => !controlByKey.get(f.key)?.required,\n );\n const optionalMissing = controls\n .filter((c) => !c.hidden && !c.required && !filledKeys.has(c.key))\n .map((c) => resolveControlTemplates(c, templateValues));\n\n let instruction = \"\";\n // Explicit agent guidance — single instruction field\n // WHY one instruction: Avoids conflicting guidance (e.g. \"ask next\" vs \"confirm\"); priority order matches UX\n if (contextState.pendingExternalFields.length > 0) {\n // We're waiting for external confirmation (payment, signature, etc.)\n const p = contextState.pendingExternalFields[0];\n instruction = `Waiting for external action. Remind user: \"${p.instructions}\"`;\n } else if (contextState.pendingCancelConfirmation) {\n // User wants to cancel a high-effort form; confirm before losing progress\n instruction =\n \"User is trying to cancel. Confirm they really want to lose progress.\";\n } else if (contextState.uncertainFields.length > 0) {\n // Need to confirm an uncertain value before we commit it\n const u = contextState.uncertainFields[0];\n instruction = `Ask user to confirm \"${u.label}\" = \"${u.value}\".`;\n } else if (contextState.missingRequired.length > 0) {\n // Nudge for required; user can give one or several answers in one message\n instruction =\n \"Nudge the user into helping complete required fields. The user can provide one or several answers in a single message.\";\n } else if (contextState.status === \"ready\") {\n // All required fields done; suggest submit\n instruction = \"All required fields collected. Nudge user to submit.\";\n } else if (optionalMissing.length > 0) {\n // Required done; optionally nudge for optional or submit\n instruction =\n \"Required fields are done. Optionally nudge for remaining optional fields, or nudge to submit.\";\n }\n\n contextText = `form_context_json:\\n${compactJson({\n active: true,\n form_id: session.formId,\n form_name: form?.name || session.formId,\n progress: contextState.progress,\n status: contextState.status,\n required_missing: contextState.missingRequired.slice(\n 0,\n MAX_CONTEXT_FIELDS,\n ),\n required_filled: requiredFilled.map((field) => ({\n ...field,\n value: field.displayValue,\n })).slice(0, MAX_CONTEXT_FIELDS),\n optional_missing: optionalMissing.slice(0, MAX_CONTEXT_FIELDS),\n optional_filled: optionalFilled.map((field) => ({\n ...field,\n value: field.displayValue,\n })).slice(0, MAX_CONTEXT_FIELDS),\n uncertain_fields: contextState.uncertainFields\n .slice(0, MAX_CONTEXT_FIELDS)\n .map((field) => ({\n ...field,\n confidence: Math.round(field.confidence * 100) / 100,\n })),\n pending_external_fields: contextState.pendingExternalFields.map(\n (field) => ({\n ...field,\n age_minutes: Math.max(\n 0,\n Math.floor((Date.now() - field.activatedAt) / 60000),\n ),\n }),\n ).slice(0, MAX_CONTEXT_FIELDS),\n instruction,\n })}`;\n } else {\n // No active session — only stashed forms exist\n // WHY build contextState anyway: Return shape is consistent; callers get hasActiveForm: false, stashedCount; stashed list goes in text below\n contextState = {\n hasActiveForm: false,\n progress: 0,\n filledFields: [],\n missingRequired: [],\n uncertainFields: [],\n nextField: null,\n stashedCount: stashed.length,\n pendingExternalFields: [],\n };\n contextText = `form_context_json:\\n${compactJson({\n active: false,\n progress: 0,\n stashed_count: stashed.length,\n })}`;\n }\n\n // Stashed forms reminder\n // WHY: User might have forgotten about saved forms; agent can say \"You have a saved form, say resume to continue\"\n if (stashed.length > 0) {\n stashedRows = stashed.slice(0, MAX_STASHED_FOR_CONTEXT).map((s) => {\n const f = formService.getForm(s.formId);\n const ctx = formService.getSessionContext(s);\n return {\n form_id: s.formId,\n form_name: f?.name || s.formId,\n progress: ctx.progress,\n };\n });\n contextText += `\\nstashed_forms_json:\\n${compactJson(stashedRows)}`;\n contextText +=\n \"\\nstashed_instruction: User can say resume to restore one.\";\n }\n\n return {\n // Full context object for programmatic access\n // WHY: Restore action and others read data.nextField, data.filledFields, etc.\n data: JSON.parse(JSON.stringify(contextState)) as Record<\n string,\n JsonValue\n >,\n // String values for template substitution (e.g. in prompts: formContext, formProgress, formStatus)\n values: {\n formContext: contextText,\n hasActiveForm: String(contextState.hasActiveForm),\n formProgress: String(contextState.progress),\n formStatus: contextState.status || \"\",\n stashedCount: String(stashed.length),\n },\n // Human-readable text for agent (injected into prompt)\n text: contextText,\n };\n } catch (error) {\n logger.error(\"[FormContextProvider] Error:\", String(error));\n // WHY return safe fallback: Provider failure should not break response generation; agent gets empty form context\n return {\n data: { hasActiveForm: false, error: true },\n values: { formContext: 'form_context_json:\\n{\"error\":true}' },\n text: 'form_context_json:\\n{\"error\":true}',\n };\n }\n },\n};\n\nexport default formContextProvider;\n"],"mappings":"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","names":[]}
|