jsfe 1.19.0 → 1.20.0
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/README.md +17 -0
- package/dist/forms/choice-flow.js +2 -2
- package/dist/forms/declarations.js +1 -1
- package/dist/forms/flows.d.ts +2 -1
- package/dist/forms/flows.d.ts.map +1 -1
- package/dist/forms/flows.js +8 -6
- package/dist/helpers/form-conversation.d.ts +1 -0
- package/dist/helpers/form-conversation.d.ts.map +1 -1
- package/dist/helpers/form-conversation.js +24 -1
- package/dist/helpers/form-state.d.ts +2 -1
- package/dist/helpers/form-state.d.ts.map +1 -1
- package/dist/helpers/form-state.js +8 -2
- package/dist/helpers/index.d.ts +5 -1
- package/dist/helpers/index.d.ts.map +1 -1
- package/dist/helpers/index.js +5 -5
- package/dist/helpers/match-inputs.d.ts.map +1 -1
- package/dist/helpers/match-inputs.js +20 -7
- package/dist/helpers/phrases.d.ts +6 -0
- package/dist/helpers/phrases.d.ts.map +1 -1
- package/dist/helpers/phrases.js +14 -0
- package/dist/runtime-identity.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -398,6 +398,23 @@ reasonFor })` builds both executors' instructions. A reason question declares no
|
|
|
398
398
|
unchanged: the generic withdrawal includes "I no longer want it", which at a reason question withdrew three prod
|
|
399
399
|
cancellations.
|
|
400
400
|
|
|
401
|
+
**The re-ask names the customer, opt-in (jsfe 1.20.0):** `sorryNotCaught(lang, cargo)` is the acknowledgement before a
|
|
402
|
+
question asked again ("Sorry, I didn't catch that. " / "Disculpe, no le entendí. "), used by `formInputPrompt`, `ask-choice`
|
|
403
|
+
and the provider menu. A flow set that sets `cargo.sorryByName = true` (for example once the caller is verified) gets it with
|
|
404
|
+
`cargo.displayName` ("Disculpe, Ana, no le entendí. "); without it the wording is unchanged.
|
|
405
|
+
|
|
406
|
+
**Acknowledging only data, opt-in (jsfe 1.20.0):** a form declaring `acknowledge: 'data'` (or the nearest enclosing form
|
|
407
|
+
that declares it) says "Got it, your …" / "Okay, I noted your …" only for a value the caller supplied, an input whose profile
|
|
408
|
+
kind is digits, email, phone, amount, identifier, account or text. A pick among offered words (`choice`, `pattern`), such as
|
|
409
|
+
NEW CARD, is not acknowledged: the next question already shows it was understood. Rejections are always said. Without the
|
|
410
|
+
attribute every input is acknowledged as before (`formAcknowledges(cargo, reading)`).
|
|
411
|
+
|
|
412
|
+
**Digits said in groups (jsfe 1.19.1):** every `digits` input reads a comma, or a period and a space, after a group of
|
|
413
|
+
one to three digits as a pause inside one number, because speech-to-text writes spoken groups that way ("926, 1 0." is
|
|
414
|
+
the ZIP 92610, "7, 9, 984. 5." the code 799845). A comma after a longer number still separates two numbers ("92610,
|
|
415
|
+
56035214"). At its own question, an answer of digits and separators alone is one number when its length fits ("4111,
|
|
416
|
+
1111, 1111, 1111"). A choice still reads "1, 2" as two keys.
|
|
417
|
+
|
|
401
418
|
**Choices that leave (`route: 'new_request'`) and staying on noise (`onNoIntent: 'stay'`, jsfe 1.15.3):** a choice
|
|
402
419
|
declared `{ key, label, route: 'new_request' }` is offered like any other, and picking it leaves like a new request
|
|
403
420
|
(intent detection decides where to). A contextual DISPATCH declared `onNoIntent: 'stay'` asks intent detection BEFORE
|
|
@@ -216,13 +216,13 @@ export const FLOWS = [
|
|
|
216
216
|
"id": "ac_retry_en",
|
|
217
217
|
"type": "SET",
|
|
218
218
|
"variable": "ac_retry",
|
|
219
|
-
"value": "typeof retry !== 'undefined' && retry ? retry :
|
|
219
|
+
"value": "typeof retry !== 'undefined' && retry ? retry : sorryNotCaught('en', cargo) + question"
|
|
220
220
|
},
|
|
221
221
|
{
|
|
222
222
|
"id": "ac_retry_spanish",
|
|
223
223
|
"type": "SET",
|
|
224
224
|
"variable": "ac_retry_es",
|
|
225
|
-
"value": "typeof retry_es !== 'undefined' && retry_es ? retry_es : '
|
|
225
|
+
"value": "typeof retry_es !== 'undefined' && retry_es ? retry_es : sorryNotCaught('es', cargo) + ac_q_es"
|
|
226
226
|
},
|
|
227
227
|
{
|
|
228
228
|
"id": "ac_exit_context",
|
|
@@ -4,7 +4,7 @@ import { linkFormSchemas, validateFlowContracts } from './contracts.js';
|
|
|
4
4
|
import { compileFormPrompts } from './prompt-compiler.js';
|
|
5
5
|
import { withSafetyRevision } from './continuation-safety.js';
|
|
6
6
|
import { capturedAuthorSpec, copyAuthorSpec, authoredExpressions } from './declaration-capture.js';
|
|
7
|
-
const formKeys = new Set(['inputs', 'before', 'after', 'confirm', 'before_confirm', 'declined', 'stop', 'close_on_stop', 'resume_on_parent_change', 'done', 'validator', 'confirmation_condition', 'prompt', 'prompt_es', 'incomplete_outcome', 'on_exhausted', 'on_no', 'must_complete']);
|
|
7
|
+
const formKeys = new Set(['inputs', 'before', 'after', 'confirm', 'before_confirm', 'declined', 'stop', 'close_on_stop', 'resume_on_parent_change', 'done', 'validator', 'confirmation_condition', 'prompt', 'prompt_es', 'incomplete_outcome', 'on_exhausted', 'on_no', 'must_complete', 'acknowledge']);
|
|
8
8
|
const sharedKeys = ['id', 'name', 'description', 'version', 'scope', 'primary', 'parameters', 'contract', 'parameterInstructions'];
|
|
9
9
|
const safe = /^[A-Za-z_$][\w$]*$/;
|
|
10
10
|
const fail = message => { throw new Error('Flow declaration: ' + message); };
|
package/dist/forms/flows.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export function formFlow({ id, name, version, description, inputs, after, extraVars, parameters, confirm, before_confirm, declined, stop, close_on_stop, on_no, resume_on_parent_change, before, done, primary, scope, validator, confirmation_condition, prompt, prompt_es, incomplete_outcome, on_exhausted, must_complete }: {
|
|
1
|
+
export function formFlow({ id, name, version, description, inputs, after, extraVars, parameters, confirm, before_confirm, declined, stop, close_on_stop, on_no, resume_on_parent_change, before, done, primary, scope, validator, confirmation_condition, prompt, prompt_es, incomplete_outcome, on_exhausted, must_complete, acknowledge }: {
|
|
2
2
|
id: any;
|
|
3
3
|
name: any;
|
|
4
4
|
version: any;
|
|
@@ -25,6 +25,7 @@ export function formFlow({ id, name, version, description, inputs, after, extraV
|
|
|
25
25
|
incomplete_outcome?: null | undefined;
|
|
26
26
|
on_exhausted?: null | undefined;
|
|
27
27
|
must_complete?: boolean | undefined;
|
|
28
|
+
acknowledge?: string | undefined;
|
|
28
29
|
}): {
|
|
29
30
|
variables: {
|
|
30
31
|
[k: string]: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"flows.d.ts","sourceRoot":"","sources":["../../src/forms/flows.js"],"names":[],"mappings":"AAqIA
|
|
1
|
+
{"version":3,"file":"flows.d.ts","sourceRoot":"","sources":["../../src/forms/flows.js"],"names":[],"mappings":"AAqIA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA8DC;AACD;;;GAGG;AACH;;;;;;EAGC;AAwfD,wHAAwH;AACxH,wEAaC;AACD,4FAA4F;AAC5F;;;EAYC;AACD,sGAAsG;AACtG;;;EAOC;AA9rBM,6CAAuG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAu3B9G;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAOG"}
|
package/dist/forms/flows.js
CHANGED
|
@@ -127,8 +127,10 @@ function validateAcceptanceDeclarations(id, inputs) {
|
|
|
127
127
|
else { /* no invites, or a list of names */ }
|
|
128
128
|
}
|
|
129
129
|
}
|
|
130
|
-
export function formFlow({ id, name, version, description, inputs, after = [], extraVars = {}, parameters = [], confirm = null, before_confirm = [], declined = [], stop = '', close_on_stop = false, on_no = '', resume_on_parent_change = false, before = [], done = true, primary = false, scope = 'isolated', validator = '', confirmation_condition = 'true', prompt = undefined, prompt_es = undefined, incomplete_outcome = null, on_exhausted = null, must_complete = false }) {
|
|
130
|
+
export function formFlow({ id, name, version, description, inputs, after = [], extraVars = {}, parameters = [], confirm = null, before_confirm = [], declined = [], stop = '', close_on_stop = false, on_no = '', resume_on_parent_change = false, before = [], done = true, primary = false, scope = 'isolated', validator = '', confirmation_condition = 'true', prompt = undefined, prompt_es = undefined, incomplete_outcome = null, on_exhausted = null, must_complete = false, acknowledge = 'all' }) {
|
|
131
131
|
validateFormExhaustion(on_exhausted);
|
|
132
|
+
if (acknowledge !== 'all' && acknowledge !== 'data')
|
|
133
|
+
throw new Error(`FORM ${id}: acknowledge must be 'all' or 'data'`);
|
|
132
134
|
if (on_no !== '' && (typeof on_no !== 'string' || !/^[\w-]+$/.test(on_no)))
|
|
133
135
|
throw new Error(`FORM ${id}: on_no must be a flow id`);
|
|
134
136
|
if (typeof must_complete !== 'boolean')
|
|
@@ -162,7 +164,7 @@ export function formFlow({ id, name, version, description, inputs, after = [], e
|
|
|
162
164
|
...(hasFormResponse(inputs) ? [{ ...SET(`${id}_response_reader_required`, 'noop', "typeof validateFormResponse === 'function'"), requireTrue: true }] : []),
|
|
163
165
|
...proposalRuntimeSteps(id, [], hasInputProposals(inputs) ? [INPUT_PROPOSALS_REQUIREMENT] : []),
|
|
164
166
|
SET(`${id}_resume_kind`, 'form_internal_resume', '!!form_instance'),
|
|
165
|
-
SET(`${id}_open`, 'form_instance', `openFormInstance(cargo, ${JSON.stringify(id)}, inputs, { parent: parent_form || null, input: result_input || '', context:!!parent_form && !result_input, resume_on_parent_change: ${resume_on_parent_change === true}, resume: form_instance || (!parent_form ? (cargo.formTasks || {})[${JSON.stringify(id)}] || '' : ''), providers: provides, questions: questions, incomplete_outcome: ${incompleteExpression}, on_exhausted: form_on_exhausted, on_no: form_on_no, must_complete: form_must_complete })`),
|
|
167
|
+
SET(`${id}_open`, 'form_instance', `openFormInstance(cargo, ${JSON.stringify(id)}, inputs, { parent: parent_form || null, input: result_input || '', context:!!parent_form && !result_input, resume_on_parent_change: ${resume_on_parent_change === true}, resume: form_instance || (!parent_form ? (cargo.formTasks || {})[${JSON.stringify(id)}] || '' : ''), providers: provides, questions: questions, incomplete_outcome: ${incompleteExpression}, on_exhausted: form_on_exhausted, on_no: form_on_no, must_complete: form_must_complete${acknowledge === 'data' ? ", acknowledge: 'data'" : ''} })`),
|
|
166
168
|
SIDE(`${id}_task`, `(!parent_form ? (cargo.formTasks = cargo.formTasks || {}, cargo.formTasks[${JSON.stringify(id)}] = form_instance) : true, true)`),
|
|
167
169
|
SIDE(`${id}_provider_reset`, '(delete cargo.forms[form_instance].providerRequests, delete cargo.forms[form_instance].activeGates, delete cargo.forms[form_instance].providerGateActive, true)'),
|
|
168
170
|
SIDE(`${id}_pass_reset`, '(cargo.forms[form_instance].replays = 0, form_internal_resume ? true : (cargo.forms[form_instance].attempts = {},cargo.forms[form_instance].misses={},cargo.forms[form_instance].feedback={}), true)'),
|
|
@@ -521,8 +523,8 @@ const acceptedEffects = {
|
|
|
521
523
|
WHEN('ie_none', '!ca_h', END('ie_end')),
|
|
522
524
|
SET('ie_rest', 'ca_rest', 'readings.slice(1)'),
|
|
523
525
|
WHEN('ie_reject', "ca_h.outcome === 'rejected'", SIDE('ie_attempt', "(cargo.forms[ca_h.form].attempts[ca_h.asked || ca_h.name] = Number(cargo.forms[ca_h.form].attempts[ca_h.asked || ca_h.name] || 0) + 1, cargo.forms[ca_h.form].feedback=cargo.forms[ca_h.form].feedback || {}, cargo.forms[ca_h.form].feedback[ca_h.asked || ca_h.name]='invalid', true)")),
|
|
524
|
-
SET('ie_say_en', 'ca_ack', "ca_h.outcome === 'rejected' && ca_h.quiet ? '' : ca_h.outcome === 'rejected' ? (ca_h.message || ('That ' + (ca_h.label || ca_h.name) + \" doesn't look right.\")) : ca_h.silent === true || ca_h.fromPending ? '' : ca_h.outcome === 'known' && !ca_h.current ? ('Thank you, I already have your ' + ca_h.label + '.') : ca_h.outcome === 'changed' && ca_h.origin !== 'activation' ? ('Okay, I noted your ' + ca_h.label + '.') : (ca_h.outcome === 'taken' || ca_h.outcome === 'pending') && ca_h.origin !== 'activation' ? ('Got it, your ' + ca_h.label + '.') : ''"),
|
|
525
|
-
SET('ie_say_es', 'ca_ack_es', "ca_h.outcome === 'rejected' && ca_h.quiet ? '' : ca_h.outcome === 'rejected' ? (ca_h.message_es || ca_h.message || ('Ese ' + (ca_h.label_es || ca_h.label || ca_h.name) + ' no parece correcto.')) : ca_h.silent === true || ca_h.fromPending ? '' : ca_h.outcome === 'known' && !ca_h.current ? ('Gracias, ya tengo su ' + ca_h.label_es + '.') : ca_h.outcome === 'changed' && ca_h.origin !== 'activation' ? ('Listo, anoté su ' + ca_h.label_es + '.') : (ca_h.outcome === 'taken' || ca_h.outcome === 'pending') && ca_h.origin !== 'activation' ? ('Entendido, su ' + ca_h.label_es + '.') : ''"),
|
|
526
|
+
SET('ie_say_en', 'ca_ack', "ca_h.outcome === 'rejected' && ca_h.quiet ? '' : ca_h.outcome === 'rejected' ? (ca_h.message || ('That ' + (ca_h.label || ca_h.name) + \" doesn't look right.\")) : ca_h.silent === true || ca_h.fromPending || !formAcknowledges(cargo, ca_h) ? '' : ca_h.outcome === 'known' && !ca_h.current ? ('Thank you, I already have your ' + ca_h.label + '.') : ca_h.outcome === 'changed' && ca_h.origin !== 'activation' ? ('Okay, I noted your ' + ca_h.label + '.') : (ca_h.outcome === 'taken' || ca_h.outcome === 'pending') && ca_h.origin !== 'activation' ? ('Got it, your ' + ca_h.label + '.') : ''"),
|
|
527
|
+
SET('ie_say_es', 'ca_ack_es', "ca_h.outcome === 'rejected' && ca_h.quiet ? '' : ca_h.outcome === 'rejected' ? (ca_h.message_es || ca_h.message || ('Ese ' + (ca_h.label_es || ca_h.label || ca_h.name) + ' no parece correcto.')) : ca_h.silent === true || ca_h.fromPending || !formAcknowledges(cargo, ca_h) ? '' : ca_h.outcome === 'known' && !ca_h.current ? ('Gracias, ya tengo su ' + ca_h.label_es + '.') : ca_h.outcome === 'changed' && ca_h.origin !== 'activation' ? ('Listo, anoté su ' + ca_h.label_es + '.') : (ca_h.outcome === 'taken' || ca_h.outcome === 'pending') && ca_h.origin !== 'activation' ? ('Entendido, su ' + ca_h.label_es + '.') : ''"),
|
|
526
528
|
WHEN('ie_say', 'ca_ack', SAY('ie_ack', '{{ca_ack}}', '{{ca_ack_es}}')),
|
|
527
529
|
WHEN('ie_callback', "ca_h.after && ca_h.outcome !== 'known' && ca_h.outcome !== 'rejected' && ca_h.outcome !== 'pending'", FLOW('ie_after', '{{ca_h.after}}', 'call', { form: '{{ca_h.form}}', input: '{{ca_h.name}}', value: '{{ca_h.value}}' })),
|
|
528
530
|
WHEN('ie_more', 'ca_rest.length > 0', FLOW('ie_next', 'input-accepted-effects', 'replace', { readings: '{{ca_rest}}' })),
|
|
@@ -809,8 +811,8 @@ const providerMenu = {
|
|
|
809
811
|
variables: vars({ retry: '', pm_q: '', pm_q_es: '', pm_choices: [], pm_key: '', pm_keys: [], pm_close: false, pm_flow: '', pm_bye: '', pm_bye_es: '', pm_who: '', pm_sorry: '', pm_sorry_es: '' }),
|
|
810
812
|
steps: [
|
|
811
813
|
SET('pm_name', 'pm_who', NAME_JS),
|
|
812
|
-
SET('pm_question', 'pm_q', `(function () { ${RENDER_JS} var m = provides.menu; var t = cargo.voice && m.question_voice ? m.question_voice : m.question; return (retry === '1' ?
|
|
813
|
-
SET('pm_question_es', 'pm_q_es', `(function () { ${RENDER_JS} var m = provides.menu; var t = cargo.voice && (m.question_voice_es || m.question_voice) ? (m.question_voice_es || m.question_voice) : (m.question_es || m.question); return (retry === '1' ? '
|
|
814
|
+
SET('pm_question', 'pm_q', `(function () { ${RENDER_JS} var m = provides.menu; var t = cargo.voice && m.question_voice ? m.question_voice : m.question; return (retry === '1' ? sorryNotCaught('en', cargo) : '') + fill(t, {}, null, 'en', { name: pm_who, verb: cargo.verb || (cargo.voice ? 'say' : 'type') }); })()`),
|
|
815
|
+
SET('pm_question_es', 'pm_q_es', `(function () { ${RENDER_JS} var m = provides.menu; var t = cargo.voice && (m.question_voice_es || m.question_voice) ? (m.question_voice_es || m.question_voice) : (m.question_es || m.question); return (retry === '1' ? sorryNotCaught('es', cargo) : '') + fill(t, {}, null, 'es', { name: pm_who, verb: cargo.verb_es || (cargo.voice ? 'diga' : 'escriba') }); })()`),
|
|
814
816
|
SET('pm_build', 'pm_choices', "provides.facts.concat(provides.sets || []).filter(function (f) { return Array.isArray(f.phrases ?? f.words) && (f.phrases ?? f.words).length; }).map(function (f) { return { key: 'fact:' + f.key, phrases: f.phrases ?? f.words }; }).concat((provides.menu.choices || []).map(function (c) { return { key: 'act:' + c.key, phrases: c.phrases ?? c.words, exact: c.exact === true }; })).concat(provides.menu.done ? [{ key: 'done', phrases: provides.menu.done.phrases ?? provides.menu.done.words, exact: true }] : [])"),
|
|
815
817
|
FLOW('pm_ask', 'ask-choice', 'call', { question: '{{pm_q}}', question_es: '{{pm_q_es}}', choices: '{{pm_choices}}', accept_other: true, digits: { min: 1, max: 1 }, exit_context: '{{provides.subject}}', exit_context_es: '{{provides.subject_es}}' }),
|
|
816
818
|
// A declared terminal phrase (for example "no thanks") may also fit a shorter action
|
|
@@ -9,6 +9,7 @@ export function formMustComplete(cargo: any, id: any, input: any): boolean;
|
|
|
9
9
|
* question_text / retry_text (and _es): the written-channel wording, used only when the conversation is not a call.
|
|
10
10
|
*/
|
|
11
11
|
export function formInputPrompt(cargo: any, id: any, input: any, spec: any, data?: {}, language?: string): any;
|
|
12
|
+
export function formAcknowledges(cargo: any, reading: any): boolean;
|
|
12
13
|
export function consumeFormFeedback(cargo: any, id: any, input: any): boolean;
|
|
13
14
|
/** Recovery refers to the actual owning root, and never permits a fresh attempt at an unknown action. */
|
|
14
15
|
export function formConversationOutcome(cargo: any, id: any, reason: any, failure?: boolean, recoveryMode?: string): {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"form-conversation.d.ts","sourceRoot":"","sources":["../../src/helpers/form-conversation.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"form-conversation.d.ts","sourceRoot":"","sources":["../../src/helpers/form-conversation.js"],"names":[],"mappings":"AAMA,0EAGC;AACD,oEAGC;AACD;;qGAEqG;AACrG,2EAWC;AACD;;;GAGG;AACH,+GAQC;AAKD,oEAOC;AACD,8EAAwI;AAExI,yGAAyG;AACzG;;;;;;;;;;;;;;;;;;;;EAUC;AAED,sGAAsG;AACtG;;;;;;;;;;;;;;;;;;;;EAeC;AAED;2HAC2H;AAC3H,8EAOC"}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { formIncompleteOutcome, closeFormInstance } from './form-state.js';
|
|
3
3
|
import { isWholeNo } from './answer.js';
|
|
4
4
|
import { readInputProfile } from './match-inputs.js';
|
|
5
|
+
import { sorryNotCaught } from './phrases.js';
|
|
5
6
|
export function clearFormMisses(cargo, id, input) {
|
|
6
7
|
const state = cargo.forms?.[id];
|
|
7
8
|
if (!state)
|
|
@@ -56,7 +57,29 @@ export function formInputPrompt(cargo, id, input, spec, data = {}, language = 'e
|
|
|
56
57
|
return es ? 'Continúe.' : 'Go on.';
|
|
57
58
|
if (feedback && retry)
|
|
58
59
|
return retry;
|
|
59
|
-
return (feedback === 'miss' ? (es ? '
|
|
60
|
+
return (feedback === 'miss' ? sorryNotCaught(es ? 'es' : 'en', cargo) : '') + question;
|
|
61
|
+
}
|
|
62
|
+
/** Whether an accepted input is acknowledged ("Got it, your amount."). A form declaring acknowledge: 'data' (jsfe 1.20.0,
|
|
63
|
+
* owner 2026-10-31), or the nearest enclosing form that declares it, acknowledges only a value the caller supplied (digits,
|
|
64
|
+
* email, phone, amount, identifier, account, text), never a pick among offered words (choice, pattern). Default: every input. */
|
|
65
|
+
const DATA_KINDS = new Set(['digits', 'email', 'phone', 'amount', 'identifier', 'account', 'text']);
|
|
66
|
+
export function formAcknowledges(cargo, reading) {
|
|
67
|
+
const seen = new Set();
|
|
68
|
+
let id = reading && reading.form, mode = 'all';
|
|
69
|
+
while (id && cargo.forms?.[id] && !seen.has(id)) {
|
|
70
|
+
seen.add(id);
|
|
71
|
+
const state = cargo.forms[id];
|
|
72
|
+
if (state.acknowledge) {
|
|
73
|
+
mode = state.acknowledge;
|
|
74
|
+
break;
|
|
75
|
+
}
|
|
76
|
+
id = state.parent;
|
|
77
|
+
}
|
|
78
|
+
if (mode !== 'data')
|
|
79
|
+
return true;
|
|
80
|
+
const state = cargo.forms[reading.form] || {}, group = (state.specs || []).find(s => s.name === reading.name || s.any_of?.some(i => i.name === reading.name));
|
|
81
|
+
const spec = group?.any_of?.find(i => i.name === reading.name) || group, kind = spec?.profile?.kind;
|
|
82
|
+
return DATA_KINDS.has(kind);
|
|
60
83
|
}
|
|
61
84
|
export function consumeFormFeedback(cargo, id, input) { if (cargo.forms?.[id]?.feedback)
|
|
62
85
|
delete cargo.forms[id].feedback[input]; return true; }
|
|
@@ -18,7 +18,7 @@ export function settleFormActivations(batch?: {}, cargo?: null): {
|
|
|
18
18
|
pending: any[];
|
|
19
19
|
};
|
|
20
20
|
/** Explicit ownership: unrelated tasks and sibling inputs never share an instance by definition id. */
|
|
21
|
-
export function openFormInstance(cargo: any, definition: any, specs: any, { parent, input, resume, task, providers, questions, context, resume_on_parent_change, incomplete_outcome, on_exhausted, on_no, must_complete }?: {
|
|
21
|
+
export function openFormInstance(cargo: any, definition: any, specs: any, { parent, input, resume, task, providers, questions, context, resume_on_parent_change, incomplete_outcome, on_exhausted, on_no, acknowledge, must_complete }?: {
|
|
22
22
|
parent?: null | undefined;
|
|
23
23
|
input?: string | undefined;
|
|
24
24
|
resume?: string | undefined;
|
|
@@ -30,6 +30,7 @@ export function openFormInstance(cargo: any, definition: any, specs: any, { pare
|
|
|
30
30
|
incomplete_outcome?: undefined;
|
|
31
31
|
on_exhausted?: undefined;
|
|
32
32
|
on_no?: undefined;
|
|
33
|
+
acknowledge?: undefined;
|
|
33
34
|
must_complete?: undefined;
|
|
34
35
|
}): any;
|
|
35
36
|
/** An incomplete optional follow-up may preserve an explicitly completed task outcome. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"form-state.d.ts","sourceRoot":"","sources":["../../src/helpers/form-state.js"],"names":[],"mappings":"AAQA,qDAKC;AAED,oDAEC;AAED,qFAAqF;AACrF,oEA8BC;AA0CD,2FAEC;AACD,8FAcC;AACD,2DAOC;AACD,uEA6CC;AAED,kGAAkG;AAClG,2GAOC;AACD,0HAA0H;AAC1H,uEAwBC;AACD,8HAA8H;AAC9H;;;;;EAWC;AACD,uGAAuG;AACvG
|
|
1
|
+
{"version":3,"file":"form-state.d.ts","sourceRoot":"","sources":["../../src/helpers/form-state.js"],"names":[],"mappings":"AAQA,qDAKC;AAED,oDAEC;AAED,qFAAqF;AACrF,oEA8BC;AA0CD,2FAEC;AACD,8FAcC;AACD,2DAOC;AACD,uEA6CC;AAED,kGAAkG;AAClG,2GAOC;AACD,0HAA0H;AAC1H,uEAwBC;AACD,8HAA8H;AAC9H;;;;;EAWC;AACD,uGAAuG;AACvG;;;;;;;;;;;;;;QAuCC;AAED,0FAA0F;AAC1F;;;SAIC;AACD,gGAAgG;AAChG;;;;;;;;;;;EAQC;AAED,sGAAsG;AACtG,2DAIC;AACD;;;GAGG;AACH;;;;;;;SAkBC;AAED,2FAA2F;AAC3F,oGAIC;AACD,gFA4BC;AAED,2GAA2G;AAC3G;;;EASC;AACD,mEAoBC;AACD,mEAGC;AACD,sEAUC;AACD,+EAEC;AAoCD,qFAYC;AACD,oGAqCC;AACD;;;SAKC;AACD,qEAGC;AACD,gEAgBC;AACD;;uGAEuG;AACvG,sEAGC;AACD,2DAQC;AACD,oGAUC;AAED,uEAAuE;AACvE,wHA+BC;AAED,uHAAuH;AACvH,6EAaC;AACD,mHAAmH;AACnH,iGAsDC;AACD;;;GAGG;AACH;;;SAcC;AACD,+EASC;AACD,8EAeC;AAxpBM,8CAAmE"}
|
|
@@ -293,9 +293,11 @@ export function settleFormActivations(batch = {}, cargo = null) {
|
|
|
293
293
|
rejected: rejected.map(h => h.activation_inferred ? { ...h, ...h.activation_entry, activation_inferred: false } : h), pending: pending.concat(accepted.filter(h => held.has(h.activation_group))).filter(h => !discarded.has(h.activation_group)).map(h => held.has(h.activation_group) ? { ...h, ok: undefined, outcome: 'pending', silent: true } : h) };
|
|
294
294
|
}
|
|
295
295
|
/** Explicit ownership: unrelated tasks and sibling inputs never share an instance by definition id. */
|
|
296
|
-
export function openFormInstance(cargo, definition, specs, { parent = null, input = '', resume = '', task = '', providers = null, questions = null, context = false, resume_on_parent_change = undefined, incomplete_outcome = undefined, on_exhausted = undefined, on_no = undefined, must_complete = undefined } = {}) {
|
|
296
|
+
export function openFormInstance(cargo, definition, specs, { parent = null, input = '', resume = '', task = '', providers = null, questions = null, context = false, resume_on_parent_change = undefined, incomplete_outcome = undefined, on_exhausted = undefined, on_no = undefined, acknowledge = undefined, must_complete = undefined } = {}) {
|
|
297
297
|
const incomplete = validateFormIncompleteOutcome(incomplete_outcome);
|
|
298
298
|
validateFormExhaustion(on_exhausted);
|
|
299
|
+
if (acknowledge !== undefined && acknowledge !== 'all' && acknowledge !== 'data')
|
|
300
|
+
throw new Error("FORM acknowledge must be 'all' or 'data'");
|
|
299
301
|
cargo.forms ||= {};
|
|
300
302
|
const existing = resume && cargo.forms[resume];
|
|
301
303
|
if (resume && !existing)
|
|
@@ -313,6 +315,8 @@ export function openFormInstance(cargo, definition, specs, { parent = null, inpu
|
|
|
313
315
|
existing.on_no = on_no || '';
|
|
314
316
|
if (must_complete !== undefined)
|
|
315
317
|
existing.must_complete = must_complete === true;
|
|
318
|
+
if (acknowledge !== undefined)
|
|
319
|
+
existing.acknowledge = acknowledge;
|
|
316
320
|
return resume;
|
|
317
321
|
}
|
|
318
322
|
if (parent && !cargo.forms[parent])
|
|
@@ -333,13 +337,15 @@ export function openFormInstance(cargo, definition, specs, { parent = null, inpu
|
|
|
333
337
|
cargo.forms[child].on_no = on_no || '';
|
|
334
338
|
if (must_complete !== undefined)
|
|
335
339
|
cargo.forms[child].must_complete = must_complete === true;
|
|
340
|
+
if (acknowledge !== undefined)
|
|
341
|
+
cargo.forms[child].acknowledge = acknowledge;
|
|
336
342
|
return child;
|
|
337
343
|
}
|
|
338
344
|
}
|
|
339
345
|
cargo.formSequence = Number(cargo.formSequence || 0) + 1;
|
|
340
346
|
const id = `${definition}:${cargo.formSequence}`;
|
|
341
347
|
cargo.forms[id] = { id, definition, task: owner?.task || task || id, parent, result_input: input,
|
|
342
|
-
on_exhausted: on_exhausted || '', on_no: on_no || '', must_complete: must_complete === true, incomplete_outcome: incomplete, context_only: !!context, resume_on_parent_change: resume_on_parent_change === true, parent_revision: owner?.revision ?? null, specs, values: {}, accepted: {}, errors: {}, pending: {}, seeds: {}, attempts: {}, misses: {}, feedback: {}, prepared: {}, data: {}, arguments: {}, contexts: {}, children: {}, childResults: {}, revision: 0,
|
|
348
|
+
on_exhausted: on_exhausted || '', on_no: on_no || '', must_complete: must_complete === true, ...(acknowledge !== undefined ? { acknowledge } : {}), incomplete_outcome: incomplete, context_only: !!context, resume_on_parent_change: resume_on_parent_change === true, parent_revision: owner?.revision ?? null, specs, values: {}, accepted: {}, errors: {}, pending: {}, seeds: {}, attempts: {}, misses: {}, feedback: {}, prepared: {}, data: {}, arguments: {}, contexts: {}, children: {}, childResults: {}, revision: 0,
|
|
343
349
|
providers: providers || owner?.providers || null, questions: questions?.length ? questions : owner?.questions || [], status: 'collecting' };
|
|
344
350
|
if (owner) {
|
|
345
351
|
if (context) {
|
package/dist/helpers/index.d.ts
CHANGED
|
@@ -22,6 +22,7 @@ export namespace STANDARD_FUNCTIONS {
|
|
|
22
22
|
export { missFormInput };
|
|
23
23
|
export { formMustComplete };
|
|
24
24
|
export { formInputPrompt };
|
|
25
|
+
export { formAcknowledges };
|
|
25
26
|
export { consumeFormFeedback };
|
|
26
27
|
export { formConversationOutcome };
|
|
27
28
|
export { readMoneyAmount };
|
|
@@ -53,6 +54,7 @@ export namespace STANDARD_FUNCTIONS {
|
|
|
53
54
|
export { anythingElse };
|
|
54
55
|
export { goodbye };
|
|
55
56
|
export { sayName };
|
|
57
|
+
export { sorryNotCaught };
|
|
56
58
|
export { dateToWords };
|
|
57
59
|
export { amountToSpeech };
|
|
58
60
|
export { validateCardNumber };
|
|
@@ -141,6 +143,7 @@ import { formNoOwner } from './form-conversation.js';
|
|
|
141
143
|
import { missFormInput } from './form-conversation.js';
|
|
142
144
|
import { formMustComplete } from './form-conversation.js';
|
|
143
145
|
import { formInputPrompt } from './form-conversation.js';
|
|
146
|
+
import { formAcknowledges } from './form-conversation.js';
|
|
144
147
|
import { consumeFormFeedback } from './form-conversation.js';
|
|
145
148
|
import { formConversationOutcome } from './form-conversation.js';
|
|
146
149
|
import { readMoneyAmount } from './money.js';
|
|
@@ -174,6 +177,7 @@ import { normalizeAndFindCapture } from './capture.js';
|
|
|
174
177
|
import { anythingElse } from './phrases.js';
|
|
175
178
|
import { goodbye } from './phrases.js';
|
|
176
179
|
import { sayName } from './phrases.js';
|
|
180
|
+
import { sorryNotCaught } from './phrases.js';
|
|
177
181
|
import { dateToWords } from './date-words.js';
|
|
178
182
|
import { amountToSpeech } from './amount-to-speech.js';
|
|
179
183
|
import { validateCardNumber } from './card-checks.js';
|
|
@@ -243,5 +247,5 @@ import { validatePhone } from './validators.js';
|
|
|
243
247
|
import { validateEmail } from './validators.js';
|
|
244
248
|
import { urlToSpeech } from './url-speech.js';
|
|
245
249
|
import { textWithUrlToSpeech } from './url-speech.js';
|
|
246
|
-
export { continuationSafetyProtocol, cancelFormTask, contextualResponseProtocol, validateFormResponse, hasFormResponse, formProposalPolicy, stageFormProposal, nextFormProposal, formProposalReadings, bindFormProposal, decideFormProposal, commitFormProposals, settleFormProposalValidation, formProposalProtocol, validateFormProposalPolicy, validateFormProposalDescriptor, formProposalDescriptor, clearFormMisses, formNoOwner, missFormInput, formMustComplete, formInputPrompt, consumeFormFeedback, formConversationOutcome, readMoneyAmount, isMoneyAmount, moneyIntentAllows, isPastPaymentStatement, isPartialMoneyAnswer, validateMoneyOptions, takeChoiceOffer, matchesChoice, ANSWER_WORDS, CONFIRM_WORDS, isAnswer, isAgentRequest, isExitRequest, isOtherRequest, isSpelledFragment, isAmountAnswer, amountInAnswer, pickChoice, readEmail, readPhone, readInternationalPhone, readIdentifier, last4, maskEmail, callerTextNumber, textDestination, callerIsInternational, normalizeAndFindCapture, anythingElse, goodbye, sayName, dateToWords, amountToSpeech, validateCardNumber, validateExpiration, consumeActionReceipt, authorizeFormActionRetry, formSignature, selectFormAction, confirmFormRevision, prepareFormAction, actionConfirmationValid, beginFormAction, finishFormAction, actionGateVerified, sourceNeedsLoad, beginSourceRefresh, completeSourceRefresh, pickProviderQuestion, matchInputs, readFormAnswer, readInputProfile, formAcceptanceTrace, resolveFormInputs, formInputApplies, formInputFilled, nextFormInput, formInputChain, openFormInstance, validateFormIncompleteOutcome, formIncompleteOutcome, validateFormExhaustion, exhaustFormInput, formInitialReadings, orderFormReadings, proposedFormValues, applyFormValidation, formReadingDependencies, exclusiveFormReadings, enrichFormMatches, expandFormActivations, settleFormActivations, rememberFormArguments, resetFormInputs, hasFormValue, formPath, formValuesEqual, formReadingReady, formReadingRelevant, formPreparation, queueFormReadings, pendingFormReadings, formWorkLimit, answerResetsFormReplays, rejectFormReadings, invalidateFormInputs, acceptFormReadings, formInstanceApplies, formParentResume, closeFormInstance, checkOtp, clearOtp, maskDestination, validateOTP, OTP_EXPIRY_MS, validateDigits, validatePhone, validateEmail, urlToSpeech, textWithUrlToSpeech };
|
|
250
|
+
export { continuationSafetyProtocol, cancelFormTask, contextualResponseProtocol, validateFormResponse, hasFormResponse, formProposalPolicy, stageFormProposal, nextFormProposal, formProposalReadings, bindFormProposal, decideFormProposal, commitFormProposals, settleFormProposalValidation, formProposalProtocol, validateFormProposalPolicy, validateFormProposalDescriptor, formProposalDescriptor, clearFormMisses, formNoOwner, missFormInput, formMustComplete, formInputPrompt, formAcknowledges, consumeFormFeedback, formConversationOutcome, readMoneyAmount, isMoneyAmount, moneyIntentAllows, isPastPaymentStatement, isPartialMoneyAnswer, validateMoneyOptions, takeChoiceOffer, matchesChoice, ANSWER_WORDS, CONFIRM_WORDS, isAnswer, isAgentRequest, isExitRequest, isOtherRequest, isSpelledFragment, isAmountAnswer, amountInAnswer, pickChoice, readEmail, readPhone, readInternationalPhone, readIdentifier, last4, maskEmail, callerTextNumber, textDestination, callerIsInternational, normalizeAndFindCapture, anythingElse, goodbye, sayName, sorryNotCaught, dateToWords, amountToSpeech, validateCardNumber, validateExpiration, consumeActionReceipt, authorizeFormActionRetry, formSignature, selectFormAction, confirmFormRevision, prepareFormAction, actionConfirmationValid, beginFormAction, finishFormAction, actionGateVerified, sourceNeedsLoad, beginSourceRefresh, completeSourceRefresh, pickProviderQuestion, matchInputs, readFormAnswer, readInputProfile, formAcceptanceTrace, resolveFormInputs, formInputApplies, formInputFilled, nextFormInput, formInputChain, openFormInstance, validateFormIncompleteOutcome, formIncompleteOutcome, validateFormExhaustion, exhaustFormInput, formInitialReadings, orderFormReadings, proposedFormValues, applyFormValidation, formReadingDependencies, exclusiveFormReadings, enrichFormMatches, expandFormActivations, settleFormActivations, rememberFormArguments, resetFormInputs, hasFormValue, formPath, formValuesEqual, formReadingReady, formReadingRelevant, formPreparation, queueFormReadings, pendingFormReadings, formWorkLimit, answerResetsFormReplays, rejectFormReadings, invalidateFormInputs, acceptFormReadings, formInstanceApplies, formParentResume, closeFormInstance, checkOtp, clearOtp, maskDestination, validateOTP, OTP_EXPIRY_MS, validateDigits, validatePhone, validateEmail, urlToSpeech, textWithUrlToSpeech };
|
|
247
251
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/helpers/index.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/helpers/index.js"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2CACyC,iCAAiC;+BAI2F,wBAAwB;2CAH/G,0BAA0B;qCAA1B,0BAA0B;gCAA1B,0BAA0B;mCACsE,qBAAqB;kCAArB,qBAAqB;iCAArB,qBAAqB;qCAArB,qBAAqB;iCAArB,qBAAqB;mCAArB,qBAAqB;oCAArB,qBAAqB;6CAArB,qBAAqB;qCAC/E,wBAAwB;2CAAxB,wBAAwB;+CAAxB,wBAAwB;uCAAxB,wBAAwB;gCACyB,wBAAwB;4BAAxB,wBAAwB;8BAAxB,wBAAwB;iCAAxB,wBAAwB;gCAAxB,wBAAwB;iCAAxB,wBAAwB;oCAAxB,wBAAwB;wCAAxB,wBAAwB;gCAmB9D,YAAY;8BAAZ,YAAY;kCAAZ,YAAY;uCAAZ,YAAY;qCAAZ,YAAY;qCAAZ,YAAY;gCAlB3G,mBAAmB;8BAiBrB,qBAAqB;6BAEiH,aAAa;8BAAb,aAAa;yBAAb,aAAa;+BAAb,aAAa;8BAAb,aAAa;+BAAb,aAAa;kCAAb,aAAa;+BAAb,aAAa;+BAAb,aAAa;2BAAb,aAAa;0BACxB,cAAc;0BAAd,cAAc;uCAAd,cAAc;+BAAd,cAAc;sBAAd,cAAc;0BAAd,cAAc;iCAAd,cAAc;gCAAd,cAAc;sCAAd,cAAc;wCAC/H,cAAc;6BACS,cAAc;wBAAd,cAAc;wBAAd,cAAc;+BAAd,cAAc;4BACjD,iBAAiB;+BACd,uBAAuB;mCACC,kBAAkB;mCAAlB,kBAAkB;qCAxB+I,mBAAmB;yCAAnB,mBAAmB;8BAAnB,mBAAmB;iCAAnB,mBAAmB;oCAAnB,mBAAmB;kCAAnB,mBAAmB;wCAAnB,mBAAmB;gCAAnB,mBAAmB;iCAAnB,mBAAmB;mCAAnB,mBAAmB;gCAe1I,mBAAmB;mCAAnB,mBAAmB;sCAAnB,mBAAmB;qCAAnB,mBAAmB;4BAUjC,mBAAmB;+BAAnB,mBAAmB;iCAAnB,mBAAmB;oCAAnB,mBAAmB;kCACkoB,iBAAiB;iCAAjB,iBAAiB;gCAAjB,iBAAiB;8BAAjB,iBAAiB;+BAAjB,iBAAiB;iCAAjB,iBAAiB;8CAAjB,iBAAiB;sCAAjB,iBAAiB;uCAAjB,iBAAiB;iCAAjB,iBAAiB;oCAAjB,iBAAiB;kCAAjB,iBAAiB;mCAAjB,iBAAiB;oCAAjB,iBAAiB;wCAAjB,iBAAiB;sCAAjB,iBAAiB;kCAAjB,iBAAiB;sCAAjB,iBAAiB;sCAAjB,iBAAiB;sCAAjB,iBAAiB;gCAAjB,iBAAiB;6BAAjB,iBAAiB;yBAAjB,iBAAiB;gCAAjB,iBAAiB;iCAAjB,iBAAiB;oCAAjB,iBAAiB;gCAAjB,iBAAiB;kCAAjB,iBAAiB;oCAAjB,iBAAiB;8BAAjB,iBAAiB;wCAAjB,iBAAiB;mCAAjB,iBAAiB;qCAAjB,iBAAiB;mCAAjB,iBAAiB;oCAAjB,iBAAiB;iCAAjB,iBAAiB;kCAAjB,iBAAiB;yBACzqB,gBAAgB;yBAAhB,gBAAgB;gCAAhB,gBAAgB;4BAAhB,gBAAgB;8BAAhB,gBAAgB;+BACnC,iBAAiB;8BAAjB,iBAAiB;8BAAjB,iBAAiB;4BAC7B,iBAAiB;oCAAjB,iBAAiB"}
|
package/dist/helpers/index.js
CHANGED
|
@@ -3,7 +3,7 @@ import { continuationSafetyProtocol } from '../forms/continuation-safety.js';
|
|
|
3
3
|
import { contextualResponseProtocol, validateFormResponse, hasFormResponse } from './contextual-response.js';
|
|
4
4
|
import { formProposalPolicy, stageFormProposal, nextFormProposal, formProposalReadings, bindFormProposal, decideFormProposal, commitFormProposals, settleFormProposalValidation } from './form-proposals.js';
|
|
5
5
|
import { formProposalProtocol, validateFormProposalPolicy, validateFormProposalDescriptor, formProposalDescriptor } from './proposal-contract.js';
|
|
6
|
-
import { cancelFormTask, clearFormMisses, formNoOwner, missFormInput, formMustComplete, formInputPrompt, consumeFormFeedback, formConversationOutcome } from './form-conversation.js';
|
|
6
|
+
import { cancelFormTask, clearFormMisses, formNoOwner, missFormInput, formMustComplete, formInputPrompt, formAcknowledges, consumeFormFeedback, formConversationOutcome } from './form-conversation.js';
|
|
7
7
|
import { takeChoiceOffer } from './choice-offer.js';
|
|
8
8
|
import { consumeActionReceipt, authorizeFormActionRetry, formSignature, selectFormAction, confirmFormRevision, prepareFormAction, actionConfirmationValid, beginFormAction, finishFormAction, actionGateVerified } from './form-actions.js';
|
|
9
9
|
/**
|
|
@@ -26,7 +26,7 @@ import { readMoneyAmount, isMoneyAmount, moneyIntentAllows, isPastPaymentStateme
|
|
|
26
26
|
import { ANSWER_WORDS, CONFIRM_WORDS, isAnswer, isAgentRequest, isExitRequest, isOtherRequest, isSpelledFragment, isAmountAnswer, amountInAnswer, pickChoice } from './answer.js';
|
|
27
27
|
import { readEmail, readPhone, readInternationalPhone, readIdentifier, last4, maskEmail, callerTextNumber, textDestination, callerIsInternational } from './contact.js';
|
|
28
28
|
import { normalizeAndFindCapture } from './capture.js';
|
|
29
|
-
import { anythingElse, goodbye, sayName } from './phrases.js';
|
|
29
|
+
import { anythingElse, goodbye, sayName, sorryNotCaught } from './phrases.js';
|
|
30
30
|
import { dateToWords } from './date-words.js';
|
|
31
31
|
import { amountToSpeech } from './amount-to-speech.js';
|
|
32
32
|
import { validateCardNumber, validateExpiration } from './card-checks.js';
|
|
@@ -35,14 +35,14 @@ import { resolveFormInputs, formInputApplies, formInputFilled, nextFormInput, fo
|
|
|
35
35
|
import { checkOtp, clearOtp, maskDestination, validateOTP, OTP_EXPIRY_MS } from './otp-check.js';
|
|
36
36
|
import { validateDigits, validatePhone, validateEmail } from './validators.js';
|
|
37
37
|
import { urlToSpeech, textWithUrlToSpeech } from './url-speech.js';
|
|
38
|
-
export { continuationSafetyProtocol, cancelFormTask, contextualResponseProtocol, validateFormResponse, hasFormResponse, formProposalPolicy, stageFormProposal, nextFormProposal, formProposalReadings, bindFormProposal, decideFormProposal, commitFormProposals, settleFormProposalValidation, formProposalProtocol, validateFormProposalPolicy, validateFormProposalDescriptor, formProposalDescriptor, clearFormMisses, formNoOwner, missFormInput, formMustComplete, formInputPrompt, consumeFormFeedback, formConversationOutcome, readMoneyAmount, isMoneyAmount, moneyIntentAllows, isPastPaymentStatement, isPartialMoneyAnswer, validateMoneyOptions, takeChoiceOffer, matchesChoice, ANSWER_WORDS, CONFIRM_WORDS, isAnswer, isAgentRequest, isExitRequest, isOtherRequest, isSpelledFragment, isAmountAnswer, amountInAnswer, pickChoice, readEmail, readPhone, readInternationalPhone, readIdentifier, last4, maskEmail, callerTextNumber, textDestination, callerIsInternational, normalizeAndFindCapture, anythingElse, goodbye, sayName, dateToWords, amountToSpeech, validateCardNumber, validateExpiration, consumeActionReceipt, authorizeFormActionRetry, formSignature, selectFormAction, confirmFormRevision, prepareFormAction, actionConfirmationValid, beginFormAction, finishFormAction, actionGateVerified, sourceNeedsLoad, beginSourceRefresh, completeSourceRefresh, pickProviderQuestion, matchInputs, readFormAnswer, readInputProfile, formAcceptanceTrace, resolveFormInputs, formInputApplies, formInputFilled, nextFormInput, formInputChain, openFormInstance, validateFormIncompleteOutcome, formIncompleteOutcome, validateFormExhaustion, exhaustFormInput, formInitialReadings, orderFormReadings, proposedFormValues, applyFormValidation, formReadingDependencies, exclusiveFormReadings, enrichFormMatches, expandFormActivations, settleFormActivations, rememberFormArguments, resetFormInputs, hasFormValue, formPath, formValuesEqual, formReadingReady, formReadingRelevant, formPreparation, queueFormReadings, pendingFormReadings, formWorkLimit, answerResetsFormReplays, rejectFormReadings, invalidateFormInputs, acceptFormReadings, formInstanceApplies, formParentResume, closeFormInstance, checkOtp, clearOtp, maskDestination, validateOTP, OTP_EXPIRY_MS, validateDigits, validatePhone, validateEmail, urlToSpeech, textWithUrlToSpeech, };
|
|
38
|
+
export { continuationSafetyProtocol, cancelFormTask, contextualResponseProtocol, validateFormResponse, hasFormResponse, formProposalPolicy, stageFormProposal, nextFormProposal, formProposalReadings, bindFormProposal, decideFormProposal, commitFormProposals, settleFormProposalValidation, formProposalProtocol, validateFormProposalPolicy, validateFormProposalDescriptor, formProposalDescriptor, clearFormMisses, formNoOwner, missFormInput, formMustComplete, formInputPrompt, formAcknowledges, consumeFormFeedback, formConversationOutcome, readMoneyAmount, isMoneyAmount, moneyIntentAllows, isPastPaymentStatement, isPartialMoneyAnswer, validateMoneyOptions, takeChoiceOffer, matchesChoice, ANSWER_WORDS, CONFIRM_WORDS, isAnswer, isAgentRequest, isExitRequest, isOtherRequest, isSpelledFragment, isAmountAnswer, amountInAnswer, pickChoice, readEmail, readPhone, readInternationalPhone, readIdentifier, last4, maskEmail, callerTextNumber, textDestination, callerIsInternational, normalizeAndFindCapture, anythingElse, goodbye, sayName, sorryNotCaught, dateToWords, amountToSpeech, validateCardNumber, validateExpiration, consumeActionReceipt, authorizeFormActionRetry, formSignature, selectFormAction, confirmFormRevision, prepareFormAction, actionConfirmationValid, beginFormAction, finishFormAction, actionGateVerified, sourceNeedsLoad, beginSourceRefresh, completeSourceRefresh, pickProviderQuestion, matchInputs, readFormAnswer, readInputProfile, formAcceptanceTrace, resolveFormInputs, formInputApplies, formInputFilled, nextFormInput, formInputChain, openFormInstance, validateFormIncompleteOutcome, formIncompleteOutcome, validateFormExhaustion, exhaustFormInput, formInitialReadings, orderFormReadings, proposedFormValues, applyFormValidation, formReadingDependencies, exclusiveFormReadings, enrichFormMatches, expandFormActivations, settleFormActivations, rememberFormArguments, resetFormInputs, hasFormValue, formPath, formValuesEqual, formReadingReady, formReadingRelevant, formPreparation, queueFormReadings, pendingFormReadings, formWorkLimit, answerResetsFormReplays, rejectFormReadings, invalidateFormInputs, acceptFormReadings, formInstanceApplies, formParentResume, closeFormInstance, checkOtp, clearOtp, maskDestination, validateOTP, OTP_EXPIRY_MS, validateDigits, validatePhone, validateEmail, urlToSpeech, textWithUrlToSpeech, };
|
|
39
39
|
/** The functions flows call by name, ready to spread into a host's approved functions. */
|
|
40
40
|
export const STANDARD_FUNCTIONS = {
|
|
41
41
|
continuationSafetyProtocol, cancelFormTask, contextualResponseProtocol, validateFormResponse, hasFormResponse,
|
|
42
42
|
formProposalPolicy, stageFormProposal, nextFormProposal, formProposalReadings, bindFormProposal, decideFormProposal, commitFormProposals, settleFormProposalValidation, formProposalProtocol, validateFormProposalPolicy, validateFormProposalDescriptor, formProposalDescriptor,
|
|
43
|
-
clearFormMisses, formNoOwner, missFormInput, formMustComplete, formInputPrompt, consumeFormFeedback, formConversationOutcome,
|
|
43
|
+
clearFormMisses, formNoOwner, missFormInput, formMustComplete, formInputPrompt, formAcknowledges, consumeFormFeedback, formConversationOutcome,
|
|
44
44
|
readMoneyAmount, isMoneyAmount, moneyIntentAllows, isPastPaymentStatement, isPartialMoneyAnswer, validateMoneyOptions, takeChoiceOffer, matchesChoice, isAnswer, isAgentRequest, isExitRequest, isOtherRequest, isSpelledFragment, isAmountAnswer, amountInAnswer, pickChoice,
|
|
45
45
|
readEmail, readPhone, readInternationalPhone, readIdentifier, last4, maskEmail, callerTextNumber, textDestination, callerIsInternational,
|
|
46
|
-
normalizeAndFindCapture, anythingElse, goodbye, sayName, dateToWords, amountToSpeech, validateCardNumber, validateExpiration, consumeActionReceipt, authorizeFormActionRetry, formSignature, selectFormAction, confirmFormRevision, prepareFormAction, actionConfirmationValid, beginFormAction, finishFormAction, actionGateVerified, sourceNeedsLoad, beginSourceRefresh, completeSourceRefresh, pickProviderQuestion, matchInputs, readFormAnswer, readInputProfile, formAcceptanceTrace, resolveFormInputs, formInputApplies, formInputFilled, nextFormInput, formInputChain, openFormInstance, validateFormIncompleteOutcome, formIncompleteOutcome, validateFormExhaustion, exhaustFormInput, formInitialReadings, orderFormReadings, proposedFormValues, applyFormValidation, formReadingDependencies, exclusiveFormReadings, enrichFormMatches, expandFormActivations, settleFormActivations, rememberFormArguments, resetFormInputs, hasFormValue, formPath, formValuesEqual, formReadingReady, formReadingRelevant, formPreparation, queueFormReadings, pendingFormReadings, formWorkLimit, answerResetsFormReplays, rejectFormReadings, invalidateFormInputs, acceptFormReadings, formInstanceApplies, formParentResume, closeFormInstance,
|
|
46
|
+
normalizeAndFindCapture, anythingElse, goodbye, sayName, sorryNotCaught, dateToWords, amountToSpeech, validateCardNumber, validateExpiration, consumeActionReceipt, authorizeFormActionRetry, formSignature, selectFormAction, confirmFormRevision, prepareFormAction, actionConfirmationValid, beginFormAction, finishFormAction, actionGateVerified, sourceNeedsLoad, beginSourceRefresh, completeSourceRefresh, pickProviderQuestion, matchInputs, readFormAnswer, readInputProfile, formAcceptanceTrace, resolveFormInputs, formInputApplies, formInputFilled, nextFormInput, formInputChain, openFormInstance, validateFormIncompleteOutcome, formIncompleteOutcome, validateFormExhaustion, exhaustFormInput, formInitialReadings, orderFormReadings, proposedFormValues, applyFormValidation, formReadingDependencies, exclusiveFormReadings, enrichFormMatches, expandFormActivations, settleFormActivations, rememberFormArguments, resetFormInputs, hasFormValue, formPath, formValuesEqual, formReadingReady, formReadingRelevant, formPreparation, queueFormReadings, pendingFormReadings, formWorkLimit, answerResetsFormReplays, rejectFormReadings, invalidateFormInputs, acceptFormReadings, formInstanceApplies, formParentResume, closeFormInstance,
|
|
47
47
|
validateOTP, validateDigits, validatePhone, validateEmail, urlToSpeech, textWithUrlToSpeech,
|
|
48
48
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"match-inputs.d.ts","sourceRoot":"","sources":["../../src/helpers/match-inputs.js"],"names":[],"mappings":"AAiBA;;;;GAIG;AACH,sEAQC;
|
|
1
|
+
{"version":3,"file":"match-inputs.d.ts","sourceRoot":"","sources":["../../src/helpers/match-inputs.js"],"names":[],"mappings":"AAiBA;;;;GAIG;AACH,sEAQC;AAuFD;;;GAGG;AACH,sFA8GC;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH;mHACmH;AACnH;;;;;;;;;;;;;;;;;;;;;EAeC;AACD;;;;;;;;;;;;;;;;;;;;;;EAoOC"}
|
|
@@ -33,10 +33,16 @@ export function formAcceptanceTrace(previous, matches) {
|
|
|
33
33
|
function unescape(answer) {
|
|
34
34
|
return String(answer ?? '').replace(/\\(['"\\])/g, '$1');
|
|
35
35
|
}
|
|
36
|
-
/**
|
|
36
|
+
/** A run of digits as said: spoken digits come apart ("4 3 2 1", "555-123-4567"), so a single space, dash or dot inside a run
|
|
37
|
+
* joins it, and so does a comma (or a period and a space) after a group of one to three digits: speech-to-text writes the
|
|
38
|
+
* pauses between spoken groups that way (prod 2026-10-31: the ZIP 92610 said as "926, 1 0.", the code as "7, 9, 984. 5."). A comma after a
|
|
39
|
+
* longer number separates two numbers ("92610, 56035214"). */
|
|
40
|
+
const DIGIT_RUN = /\d(?:[\s.-]?\d|(?<=(?:^|\D)\d{1,3})(?:,\s?|\.\s)(?=\d))*/g;
|
|
37
41
|
function digitRuns(raw) {
|
|
38
|
-
return (raw.match(
|
|
42
|
+
return (raw.match(DIGIT_RUN) || []).map((r) => r.replace(/\D/g, ''));
|
|
39
43
|
}
|
|
44
|
+
// A choice reads its keys without the comma join: "1, 2" names two keys, not 12.
|
|
45
|
+
const CHOICE_RUN = /\d(?:[\s.-]?\d)*/g;
|
|
40
46
|
const DECIMAL_AMOUNT = /(?:[$€£]\s?)?\d+[.,]\d{2}(?!\d)/g; // a written decimal amount, with its currency mark
|
|
41
47
|
/** Where the answer says amount n: a written decimal by itself ("51 72.45" is not one spoken run), else its number spans. */
|
|
42
48
|
function amountSpans(raw, n) {
|
|
@@ -130,7 +136,7 @@ export function readInputProfile(profile, answer, isCurrent = true) {
|
|
|
130
136
|
if (kind === 'choice') {
|
|
131
137
|
// An amount's own digits are the amount's: a number beside it ("51:72.45", sub account 51 and $72.45) is still a choice.
|
|
132
138
|
const free = isAmountAnswer(raw) ? amountMasked(raw) : raw;
|
|
133
|
-
const runs =
|
|
139
|
+
const runs = (free.match(CHOICE_RUN) || []).map((r) => r.replace(/\D/g, '')), joined = free.replace(CHOICE_RUN, run => ' ' + run.replace(/\D/g, '') + ' ');
|
|
134
140
|
const choices = (Array.isArray(profile.choices) ? profile.choices : []).filter(c => isCurrent || c.current_only !== true).map(c => ({ ...c,
|
|
135
141
|
numericWords: (c.phrases ?? c.words ?? []).filter(word => /^\d{2,}$/.test(String(word).trim())),
|
|
136
142
|
phrases: (c.phrases ?? c.words ?? []).concat(isCurrent ? c.keys || [] : []).filter(word => !(free !== raw && /^\d+$/.test(String(word).trim()) && !runs.includes(String(word).trim())) && (isCurrent || profile.numeric_values === true || !/^[\d*#]$/.test(String(word).trim()))) }));
|
|
@@ -208,7 +214,14 @@ export function readInputProfile(profile, answer, isCurrent = true) {
|
|
|
208
214
|
const min = Number(profile.min || 1);
|
|
209
215
|
const max = Number(profile.max || min);
|
|
210
216
|
// Out of order a decimal amount ("72.45", "287,15") is never this field's digits.
|
|
211
|
-
|
|
217
|
+
let fits = [...new Set(digitRuns(isCurrent ? raw : raw.replace(DECIMAL_AMOUNT, (m) => ' '.repeat(m.length))).filter((d) => d.length >= min && d.length <= max))];
|
|
218
|
+
// At its own question, an answer of digits and separators alone is one number when its length fits ("4111, 1111,
|
|
219
|
+
// 1111, 1111": a card read in groups of four).
|
|
220
|
+
const whole = raw.replace(/\D/g, '');
|
|
221
|
+
if (!fits.length && isCurrent && /^[\d\s.,-]+$/.test(raw.trim()) && whole.length >= min && whole.length <= max) {
|
|
222
|
+
fits = [whole];
|
|
223
|
+
}
|
|
224
|
+
else { /* read by runs */ }
|
|
212
225
|
if (fits.length === 1) {
|
|
213
226
|
return { value: fits[0], spans: numberSpans(raw, fits[0]), evidence: [`d:${fits[0]}`] };
|
|
214
227
|
}
|
|
@@ -341,7 +354,7 @@ export function matchInputs(answer, candidates, current, choiceKey) {
|
|
|
341
354
|
const repeatedChoice = known && !isCur(c) && c.profile.kind === 'choice';
|
|
342
355
|
const isChoice = !isCur(c) && c.profile.kind === 'choice';
|
|
343
356
|
const wholeOptions = isChoice ? (c.profile.choices || []).filter(choice => choice.key === option.value).map(choice => ({ ...choice, exact: true })) : [];
|
|
344
|
-
const wholeChoice = isChoice && [raw, raw.replace(
|
|
357
|
+
const wholeChoice = isChoice && [raw, raw.replace(CHOICE_RUN, run => run.replace(/\D/g, ''))].some(text => pickChoice(text, wholeOptions, false) === option.value);
|
|
345
358
|
readings.push({ form: c.form, name: c.name, value: option.value, ...(option.refused === true ? { refused: true } : {}), ...(option.incompleteMoney === true ? { incompleteMoney: true } : {}), known, repeatedChoice, wholeChoice, choice: isChoice, narrative: isCur(c) && ['text', 'choice'].includes(c.profile.kind) && c.profile.narrative === true, resetOn: Array.isArray(c.reset_on) ? c.reset_on : [], spans: option.spans, keypad: option.keypad === true, fallback: option.fallback === true,
|
|
346
359
|
...(r.alternatives ? { option_label: option.option_label, option_label_es: option.option_label_es } : {}), group: c.group, exclusive: c.exclusive === true, level: forms.indexOf(c.scope || c.form), current: isCur(c), confirm: Array.isArray(c.confirm_against) ? c.confirm_against : [] });
|
|
347
360
|
}
|
|
@@ -359,7 +372,7 @@ export function matchInputs(answer, candidates, current, choiceKey) {
|
|
|
359
372
|
// so the last four of "88 91 7112" fill nothing. A written decimal is a run of its own ("51 72.45": 51, then 72.45).
|
|
360
373
|
const decimalRuns = [...raw.matchAll(DECIMAL_AMOUNT)].map((m) => [m.index, m.index + m[0].length]);
|
|
361
374
|
const undecimal = decimalRuns.reduce((text, [a, b]) => text.slice(0, a) + ' '.repeat(b - a) + text.slice(b), raw);
|
|
362
|
-
const runs = [...undecimal.matchAll(
|
|
375
|
+
const runs = [...undecimal.matchAll(DIGIT_RUN)].map((m) => [m.index, m.index + m[0].replace(/\s+$/, '').length]).concat(decimalRuns);
|
|
363
376
|
const wholeRuns = (reading) => runs.every(([a, b]) => !(reading.spans || []).some(([s, e]) => s < b && a < e) || (reading.spans || []).some(([s, e]) => s <= a && e >= b));
|
|
364
377
|
// Rule 4: a bare number has no money mark and no word in its evidence. Written cents are a money mark: "51:72.45" at
|
|
365
378
|
// the sub account question is sub account 51 and the amount 72.45 (prod 2026-10-30).
|
|
@@ -371,7 +384,7 @@ export function matchInputs(answer, candidates, current, choiceKey) {
|
|
|
371
384
|
// A choice is excluded (a digit it does not offer reaches the invited amount, rule 4), and so is an amount.
|
|
372
385
|
const bareDigits = raw.replace(/\D/g, '');
|
|
373
386
|
const shapeOf = own && own.digits && !['choice', 'amount', 'text'].includes(own.profile.kind) ? own.digits : null;
|
|
374
|
-
if (shapeOf && bareDigits && /^[\d\s
|
|
387
|
+
if (shapeOf && bareDigits && /^[\d\s.,/-]+$/.test(raw.trim()) && !isAmountAnswer(raw) && bareDigits.length >= Number(shapeOf.min || 1) && bareDigits.length <= Number(shapeOf.max || shapeOf.min || 1)
|
|
375
388
|
&& !readings.some((reading) => reading.current || invited(reading))) {
|
|
376
389
|
readings.push({ form: own.form, name: own.name, value: bareDigits, ...(own.validator ? {} : { invalid: true }), known: false, repeatedChoice: false, wholeChoice: false, choice: false, narrative: false, resetOn: [], spans: [[0, raw.length]], keypad: false, fallback: false,
|
|
377
390
|
group: own.group, exclusive: own.exclusive === true, level: forms.indexOf(own.scope || own.form), current: true, confirm: Array.isArray(own.confirm_against) ? own.confirm_against : [] });
|
|
@@ -5,6 +5,12 @@
|
|
|
5
5
|
*/
|
|
6
6
|
/** A name as said to the customer: trimmed; an all-capitals name (as account systems store it) in title case. */
|
|
7
7
|
export function sayName(name: any): string;
|
|
8
|
+
/**
|
|
9
|
+
* The acknowledgement before a question asked again ("Sorry, I didn't catch that. "), with a trailing space. It names the
|
|
10
|
+
* customer only when the flows opt in with cargo.sorryByName (jsfe 1.20.0; owner 2026-10-31) and cargo.displayName is
|
|
11
|
+
* known; otherwise it is the wording every earlier version said.
|
|
12
|
+
*/
|
|
13
|
+
export function sorryNotCaught(lang: any, cargo: any): string;
|
|
8
14
|
/** The closing question after a task is done, in the turn's language ('en' when there is none), with the name if known. */
|
|
9
15
|
export function anythingElse(lang: any, name: any): any;
|
|
10
16
|
/** The parting wish when a conversation ends, in the turn's language, with the name if known. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"phrases.d.ts","sourceRoot":"","sources":["../../src/helpers/phrases.js"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,iHAAiH;AACjH,2CAOC;AAWD,2HAA2H;AAC3H,wDAEC;AAED,iGAAiG;AACjG,mDAEC"}
|
|
1
|
+
{"version":3,"file":"phrases.d.ts","sourceRoot":"","sources":["../../src/helpers/phrases.js"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,iHAAiH;AACjH,2CAOC;AAED;;;;GAIG;AACH,8DAOC;AAWD,2HAA2H;AAC3H,wDAEC;AAED,iGAAiG;AACjG,mDAEC"}
|
package/dist/helpers/phrases.js
CHANGED
|
@@ -13,6 +13,20 @@ export function sayName(name) {
|
|
|
13
13
|
return n;
|
|
14
14
|
}
|
|
15
15
|
}
|
|
16
|
+
/**
|
|
17
|
+
* The acknowledgement before a question asked again ("Sorry, I didn't catch that. "), with a trailing space. It names the
|
|
18
|
+
* customer only when the flows opt in with cargo.sorryByName (jsfe 1.20.0; owner 2026-10-31) and cargo.displayName is
|
|
19
|
+
* known; otherwise it is the wording every earlier version said.
|
|
20
|
+
*/
|
|
21
|
+
export function sorryNotCaught(lang, cargo) {
|
|
22
|
+
const n = cargo && cargo.sorryByName === true ? sayName(cargo.displayName) : '';
|
|
23
|
+
if (lang === 'es') {
|
|
24
|
+
return n ? `Disculpe, ${n}, no le entendí. ` : 'Disculpe, no le entendí. ';
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
return n ? `Sorry, ${n}, I didn't catch that. ` : "Sorry, I didn't catch that. ";
|
|
28
|
+
}
|
|
29
|
+
}
|
|
16
30
|
const ANYTHING_ELSE = {
|
|
17
31
|
en: (n) => (n ? `Is there anything else I can help you with, ${n}?` : 'Is there anything else I can help you with?'),
|
|
18
32
|
es: (n) => (n ? `¿Hay algo más en lo que pueda ayudarle, ${n}?` : '¿Hay algo más en lo que pueda ayudarle?'),
|
package/dist/runtime-identity.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const JSFE_RUNTIME_FINGERPRINT = "sha256:
|
|
1
|
+
export const JSFE_RUNTIME_FINGERPRINT = "sha256:69084162d369591c450f501f25553af1ce23c48c59a83f94aed895c2cb521f89";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jsfe",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.20.0",
|
|
4
4
|
"description": "TypeScript workflow engine for conversational/automation apps: multi-flow orchestration, interrupt/resume, stack-based control, robust errors, and flexible tool/REST integration.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Ron Pinkas",
|