jsfe 1.33.0 → 1.35.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/dist/forms/identity-flows.d.ts.map +1 -1
- package/dist/forms/identity-flows.js +6 -5
- package/dist/helpers/answer.d.ts.map +1 -1
- package/dist/helpers/answer.js +2 -1
- package/dist/helpers/money.js +1 -1
- package/dist/runtime-identity.js +1 -1
- package/docs/copilot-reference.md +1 -0
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"identity-flows.d.ts","sourceRoot":"","sources":["../../src/forms/identity-flows.js"],"names":[],"mappings":"AA+BA,+GAA+G;AAC/G,oFAOC;AAQD,wFAAwF;AACxF,iDAGC;AACD,6CAMC;AACD,qHAQC;AAMD,4EAKC;AA6FD,2DAOC;AACD,4DAOC;AACD,gHAAgH;AAChH,8DAGC;
|
|
1
|
+
{"version":3,"file":"identity-flows.d.ts","sourceRoot":"","sources":["../../src/forms/identity-flows.js"],"names":[],"mappings":"AA+BA,+GAA+G;AAC/G,oFAOC;AAQD,wFAAwF;AACxF,iDAGC;AACD,6CAMC;AACD,qHAQC;AAMD,4EAKC;AA6FD,2DAOC;AACD,4DAOC;AACD,gHAAgH;AAChH,8DAGC;AAqJD,8EAGC;AA4BD,4FAA4F;AAC5F;;;;EAOC;AAWD,oGAOC;AAhQD,iCAAgC,wIAAwI,CAAC;AACzK,wCAAuC,CAAC,CAAC;AA4QzC,0BAAmJ"}
|
|
@@ -150,8 +150,8 @@ const contact = worker('get-cell-or-email', 'GetCellOrEmail', 'Identify a suppli
|
|
|
150
150
|
const declined = worker('contact-caller-declined', 'ContactCallerDeclined', 'Repeat contact collection without offering caller ID.', { parameters: [param('contact_options', 'Current contact service options', 'object')], steps: [set('contact_declined_options', 'contact_options', "Object.assign({},contact_options,{caller_on_file:'no',intro:'',intro_es:''})"), contactReplay('contact_declined_repeat')] });
|
|
151
151
|
const splitFailed = worker('contact-split-failed', 'ContactSplitFailed', 'Restart a split identifier after too many digits.', { parameters: [param('contact_options', 'Current contact service options', 'object')], steps: [set('contact_split_options', 'contact_options', "Object.assign({},contact_options,{caller_on_file:'no',intro:cargo.voice?'I could not put that number together. Please enter it on your keypad.':'I could not put that number together. Please type the whole number.',intro_es:cargo.voice?'No pude juntar ese número. Por favor ingréselo con el teclado.':'No pude juntar ese número. Por favor escriba el número completo.'})"), contactReplay('contact_split_repeat')] });
|
|
152
152
|
const rejected = worker('contact-not-on-file', 'ContactNotOnFile', 'Re-collect a rejected account contact, then offer the common retry menu at the rejection limit.', { parameters: [param('contact_options', 'Current contact service options', 'object'), param('error_message', 'Rejection sentence'), param('error_message_es', 'Spanish rejection sentence')], steps: [set('contact_rejected_options', 'contact_options', "Object.assign({},contact_options,{caller_on_file:cargo.contactKind === 'caller' || contact_options.caller_on_file === 'no' ? 'no':'',fromMenu:false,intro:error_message,intro_es:error_message_es})"), when('contact_rejected_again', 'cargo.gceRejects <= 2', contactReplay('contact_rejected_collect', { gce_rejected: true })), set('contact_rejected_error', 'contact_error', 'error_message'), set('contact_rejected_error_es', 'contact_error_es', 'error_message_es'), contactMenu('contact_rejected_menu')] });
|
|
153
|
-
const AUTH_DEFAULTS = { retry_flow: '', cancel_flow: '', cell_number: '', email: '', email_validator: '', cell_validator: '', code_validator: '', code_confirm: '', answer_handler: '', retry_returns: false, retrying: false, declined_caller: false, parent_form: '', task: '', task_es: '', failed_flow: '', contact_valid: false, auth_result: false, auth_contact_error: '', auth_contact_error_es: '', auth_previous_options: null, auth_restart: false, auth_join: false, otp_expiry_ms: OTP_EXPIRY_MS };
|
|
154
|
-
const authKeys = ['retry_flow', 'cancel_flow', 'email_validator', 'cell_validator', 'code_validator', 'code_confirm', 'answer_handler', 'retry_returns', 'parent_form', 'task', 'task_es', 'failed_flow'];
|
|
153
|
+
const AUTH_DEFAULTS = { retry_flow: '', cancel_flow: '', cell_number: '', email: '', email_validator: '', cell_validator: '', code_validator: '', code_confirm: '', other_whole_answer: false, answer_handler: '', retry_returns: false, retrying: false, declined_caller: false, parent_form: '', task: '', task_es: '', failed_flow: '', contact_valid: false, auth_result: false, auth_contact_error: '', auth_contact_error_es: '', auth_previous_options: null, auth_restart: false, auth_join: false, otp_expiry_ms: OTP_EXPIRY_MS };
|
|
154
|
+
const authKeys = ['retry_flow', 'cancel_flow', 'email_validator', 'cell_validator', 'code_validator', 'code_confirm', 'other_whole_answer', 'answer_handler', 'retry_returns', 'parent_form', 'task', 'task_es', 'failed_flow'];
|
|
155
155
|
// One destination's sends, by phone (last 10 digits) or email: a caller gets 3 codes per contact (owner 2026-11-01).
|
|
156
156
|
export const OTP_DESTINATION_JS = "(cargo.otp_cell_number ? String(cargo.otp_cell_number).replace(/[^0-9]/g,'').slice(-10) : String(cargo.otp_email || '').toLowerCase())";
|
|
157
157
|
export const MAX_OTP_SENDS_PER_CONTACT = 3;
|
|
@@ -253,7 +253,7 @@ function otpPhoneUnavailable(value) {
|
|
|
253
253
|
}
|
|
254
254
|
// What the reply ranker may take at the code question beside the code: the step's offers. Email and another account are
|
|
255
255
|
// offered only where the step offers them (a code sent by text; a caller-ID preselection).
|
|
256
|
-
const OTP_REPLY_CHOICES = [{ key: 'email', label: 'use the email on the account instead of the phone' }, { key: 'another_account', label: 'use another account' },
|
|
256
|
+
const OTP_REPLY_CHOICES = [{ key: 'email', label: 'use the email on the account instead of the phone' }, { key: 'another_account', label: 'use another account' }, { key: 'no_other_account', label: 'says they have no other account, this is their only one' },
|
|
257
257
|
{ key: 'where', label: 'asks where the code was sent' }, { key: 'cannot_find', label: 'cannot find the code, or it is not there' }, { key: 'another_way', label: 'asks for another way to get the code or to verify' }, { key: 'resend', label: 'the code did not arrive; send it again' }, { key: 'no_phone', label: 'no longer has that phone or number' }, { key: '__agent', label: 'speak with a person (a live agent)' }];
|
|
258
258
|
const validateCode = worker('validate-otp-code', 'ValidateOtpCode', 'Validate a challenge response with explicit bounded retries, alternate-contact handling and tenant callbacks.', { loopBound: '3 mismatches, 3 format misses, 2 resends, 3 sends per contact', variables: vars({ otp_phrases: OTP_WORDS, email_too: false, no_code_given: false, wants_other_account: false, other_account_given: false, otp_validation_result: false, whatsapp_invite_sent: false, invite_text: '', otp_cancel: 'contact-support', answer_key: '', answer_value: '', otp_reply_choices: OTP_REPLY_CHOICES, otp_offers: [], otp_reply: {}, otp_where: '', otp_where_es: '' }), contract: { role: 'gate', summary: 'Validate the active one-time challenge', tools: ['validate-otp', 'send-whatsapp-invite'], result: { path: 'cargo.otpVerified', type: 'boolean' } }, steps: [
|
|
259
259
|
set('otp_cancel', 'otp_cancel', "cargo.authCancelFlow || 'contact-support'"),
|
|
@@ -261,7 +261,7 @@ const validateCode = worker('validate-otp-code', 'ValidateOtpCode', 'Validate a
|
|
|
261
261
|
set('otp_reset_no_code', 'no_code_given', 'false'), set('otp_reset_invite', 'whatsapp_invite_sent', 'false'), set('otp_reset_callback', 'answer_key', "''"),
|
|
262
262
|
when('otp_tenant_code', 'cargo.authOptions && cargo.authOptions.code_validator && normalized_otp_code.length !== 6 && /[0-9]/.test(user_choice)', call('otp_tenant_code_call', '{{cargo.authOptions.code_validator}}', { answer: '{{user_choice}}' })),
|
|
263
263
|
set('otp_other_given', 'other_account_given', "answer_key === 'another_account' ? (cargo.authOtherAccount=String(answer_value || ''),true) : false"),
|
|
264
|
-
set('otp_other_requested', 'wants_other_account', 'other_account_given || !!(cargo.callerPreselected && normalized_otp_code.length !== 6 && (matchesChoice(user_choice,otp_phrases.other) || otp_phrases.other_alone.includes(user_choice.replace(/[.,!?¡¿]/g,\'\').trim())))'),
|
|
264
|
+
set('otp_other_requested', 'wants_other_account', 'other_account_given || !!(cargo.callerPreselected && normalized_otp_code.length !== 6 && ((cargo.authOptions && cargo.authOptions.other_whole_answer ? pickChoice(user_choice,[{key:\'other\',phrases:otp_phrases.other,exact:true}],false) === \'other\' : matchesChoice(user_choice,otp_phrases.other)) || otp_phrases.other_alone.includes(user_choice.replace(/[.,!?¡¿]/g,\'\').trim())))'),
|
|
265
265
|
when('otp_tenant_answer', 'cargo.authOptions && cargo.authOptions.answer_handler && normalized_otp_code.length !== 6 && !wants_other_account && answer_key !== \'code_notice\'', call('otp_tenant_answer_call', '{{cargo.authOptions.answer_handler}}', { answer: '{{user_choice}}' })),
|
|
266
266
|
set('otp_where_early', 'otp_where', expression(otpWhere, 'cargo')), set('otp_where_early_es', 'otp_where_es', expression(otpWhere, 'cargo', 'true')),
|
|
267
267
|
cases('otp_response', {
|
|
@@ -294,7 +294,7 @@ const validateCode = worker('validate-otp-code', 'ValidateOtpCode', 'Validate a
|
|
|
294
294
|
// states why they called is told again how to give the code, never sent to intent detection. Then the set's declared exits
|
|
295
295
|
// ($data.gate_exits['authenticate-user'], read as $gateExits: what needs no code, e.g. a payment link), which leave as a
|
|
296
296
|
// new request.
|
|
297
|
-
set('otp_offers', 'otp_offers', "otp_reply_choices.filter(function(c){return (c.key !== 'email' || !!cargo.otp_cell_number) && (c.key !== 'another_account' || !!cargo.callerPreselected);})"
|
|
297
|
+
set('otp_offers', 'otp_offers', "otp_reply_choices.filter(function(c){return (c.key !== 'email' || !!cargo.otp_cell_number) && (c.key !== 'another_account' || !!cargo.callerPreselected) && (c.key !== 'no_other_account' || !!(cargo.callerPreselected && cargo.authOptions && cargo.authOptions.other_whole_answer));})"
|
|
298
298
|
+ ".concat(((typeof $callers !== 'undefined' && $callers && $callers['authenticate-user']) || []).map(function(c){return {key:'usecase:'+c.id,label:'states the reason for the call: '+c.label};}))"
|
|
299
299
|
+ ".concat(((typeof $gateExits !== 'undefined' && $gateExits && $gateExits['authenticate-user']) || []).map(function(a){return {key:'exit:'+a.key,label:a.label,route:'new_request'};}))"),
|
|
300
300
|
when('otp_off_topic', 'no_code_given', { id: 'otp_reply_rank', type: 'DISPATCH', onResponse: 'continue', responseContext: 'Please say the 6-digit verification code I sent you. (Por favor diga el código de verificación de 6 dígitos que le envié.)', responseChoices: 'otp_offers', responseVariable: 'otp_reply', onNoIntent: 'stay' }),
|
|
@@ -308,6 +308,7 @@ const validateCode = worker('validate-otp-code', 'ValidateOtpCode', 'Validate a
|
|
|
308
308
|
"condition: !no_code_given && otp_reply && otp_reply.key === 'another_way'": say('otp_ranked_no_other_way', '{{otp_where}} To reach a person, {{cargo.verb}} AGENT.', '{{otp_where_es}} Para hablar con una persona, {{cargo.verb_es}} AGENTE.'),
|
|
309
309
|
"condition: !no_code_given && otp_reply && otp_reply.key === 'resend'": call('otp_ranked_resend', 'resend-otp-code', { resend_reason: 'requested' }),
|
|
310
310
|
"condition: !no_code_given && otp_reply && otp_reply.key === 'no_phone'": otpMenu('otp_ranked_no_phone', "No problem. Then the code can't reach you there. You can use the email on your account instead, or {{cargo.verb}} AGENT to reach a person.", 'No hay problema. Entonces el código no le puede llegar ahí. Puede usar el correo electrónico de su cuenta, o {{cargo.verb_es}} AGENTE para hablar con una persona.', false),
|
|
311
|
+
"condition: !no_code_given && otp_reply && otp_reply.key === 'no_other_account'": say('otp_ranked_no_other_account', 'I understand. We still need to secure access to your account.', 'Entiendo. Aún necesitamos asegurar el acceso a su cuenta.'),
|
|
311
312
|
"condition: !no_code_given && otp_reply && otp_reply.key === '__agent'": call('otp_ranked_agent', 'live-agent-requested', undefined, 'reboot'),
|
|
312
313
|
default: effect('otp_reply_route_none', 'true')
|
|
313
314
|
}),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"answer.d.ts","sourceRoot":"","sources":["../../src/helpers/answer.js"],"names":[],"mappings":"AAsNA;;;GAGG;AACH,wEAkBC;AAED,qHAAqH;AACrH,+CAGC;AAED;;;;GAIG;AACH,mDAIC;AAED,+HAA+H;AAC/H,oDAIC;AAcD,2GAA2G;AAC3G,uDAGC;AAOD;;;;;GAKG;AACH,+DAUC;AAKD;;;;GAIG;AACH,oDAEC;AAuBD;;;;;;GAMG;AACH,mDAAmF;AAEnF,kGAAkG;AAClG;;;
|
|
1
|
+
{"version":3,"file":"answer.d.ts","sourceRoot":"","sources":["../../src/helpers/answer.js"],"names":[],"mappings":"AAsNA;;;GAGG;AACH,wEAkBC;AAED,qHAAqH;AACrH,+CAGC;AAED;;;;GAIG;AACH,mDAIC;AAED,+HAA+H;AAC/H,oDAIC;AAcD,2GAA2G;AAC3G,uDAGC;AAOD;;;;;GAKG;AACH,+DAUC;AAKD;;;;GAIG;AACH,oDAEC;AAuBD;;;;;;GAMG;AACH,mDAAmF;AAEnF,kGAAkG;AAClG;;;EAqCC;AAqBD,oEAqBC;AASD,gJAAgJ;AAChJ,sEAGC;AAED;;;;;;;;;GASG;AACH,6EA4GC;;;;;;;;AAhgBD,qCAKwE"}
|
package/dist/helpers/answer.js
CHANGED
|
@@ -372,7 +372,8 @@ export function numericAmountReading(input) {
|
|
|
372
372
|
t = t.replace(SPELLED_DOLLARS, (digits) => digits.replace(/\s+/g, '')).replace(IDENTIFIER_NUMBER, ' ').replace(SPELLED_DIGITS, ' ').replace(DOLLARS_JOINED_CENTS, (m, sign, d, word, joiner, c) => (sign || word || (CENTS_JOINERS.includes(joiner.toLowerCase()) && c.length === 2) ? ` $${d}.${c.padStart(2, '0')} ` : m))
|
|
373
373
|
.replace(DOLLARS_SPACE_CENTS, (m, sign, d, c) => ` $${d}.${c} `);
|
|
374
374
|
const found = [...t.matchAll(/(\$\s*)?(\d+(?:\.\d{1,2})?)(\s*(?:dollars?|d[oó]lar(?:es)?|bucks?|cents?|centavos?)\b)?/gi)]
|
|
375
|
-
|
|
375
|
+
// A proper decimal amount before the word is that amount, in dollars (prod 2026-11-03: speech-to-text's "157.95 centavos" read as $1.58; owner 2026-11-04).
|
|
376
|
+
.map((m) => ({ v: parseFloat(m[2]), marked: !!(m[1] || m[3] || m[2].includes('.')), cents: !m[1] && /cent/i.test(m[3] || '') && !/\.\d{2}$/.test(m[2]) }));
|
|
376
377
|
const values = [...new Set(found.map((f) => f.v))];
|
|
377
378
|
let pick = null;
|
|
378
379
|
if (values.length === 1) {
|
package/dist/helpers/money.js
CHANGED
|
@@ -10,7 +10,7 @@ export function validateMoneyOptions(options) {
|
|
|
10
10
|
// This is an input grammar, not a conversation classifier. Anything outside one complete
|
|
11
11
|
// monetary answer belongs to DISPATCH, including refusals, corrections with context and reports. "157 95" is dollars and
|
|
12
12
|
// cents said with no word between them (owner 2026-10-29); "95 52" is not (a card's last four read in pairs).
|
|
13
|
-
const NUMERIC_MONEY = /^(?:\$\s*)?(?:\d{1,3}(?:[,.]\d{3})+|\d+)(?:[.,]\d{1,2})?(?:\s*(?:dollars?|d[oó]lar(?:es)?|bucks?|\$))?(?:\s*(?:and|con|y)\s*(?:un\s*)?\d{1,2}(?:\s*(?:cents?|centavos?))?)?$|^\d{1,2}\s*(?:cents?|centavos?)$|^(?:\$\s*)?(?:\d|\d{3,})\s+\d{2}$/i;
|
|
13
|
+
const NUMERIC_MONEY = /^(?:\$\s*)?(?:\d{1,3}(?:[,.]\d{3})+|\d+)(?:[.,]\d{1,2})?(?:\s*(?:dollars?|d[oó]lar(?:es)?|bucks?|\$))?(?:\s*(?:and|con|y)\s*(?:un\s*)?\d{1,2}(?:\s*(?:cents?|centavos?))?)?$|^\d{1,2}\s*(?:cents?|centavos?)$|^(?:\$\s*)?(?:\d{1,3}(?:[,.]\d{3})+|\d+)[.,]\d{2}\s*(?:cents?|centavos?)$|^(?:\$\s*)?(?:\d|\d{3,})\s+\d{2}$/i;
|
|
14
14
|
const PAYMENT_PREFIX = /^(?:(?:i (?:want to|would like to|can|am able to|will) |i['’]d like to )?pay|(?:quiero |quisiera |puedo |s[ií] puedo |voy a )?(?:pagar|abonar)|(?:no,?\s+)?make it)\s*(?=\$|\d|[a-záéíóúüñ])/i;
|
|
15
15
|
// The field named with its value ("100 amount", "the amount is 100", "monto 100"): the amount, said as such.
|
|
16
16
|
const AMOUNT_LABEL_PREFIX = /^(?:(?:the|el|la)\s+)?(?:amount|monto|cantidad)(?:\s+(?:is|of|es|de))?[:\s]+(?=\$|\d)/i;
|
package/dist/runtime-identity.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const JSFE_RUNTIME_FINGERPRINT = "sha256:
|
|
1
|
+
export const JSFE_RUNTIME_FINGERPRINT = "sha256:2cf0b2dd618f57533ec1cbe3344b7269d078c415714cc6a820a2e4491ca4fb25";
|
|
@@ -217,6 +217,7 @@ Declarations:
|
|
|
217
217
|
| input `confirmation:"firm"` / `"soft"` | A yes/no decision. Firm (consent to a charge, a cancellation, storing a card, filing): YES only by `CONFIRM_WORDS` or the option's own exact phrases. Soft (an offer): the wider YES vocabulary. NO by the NO vocabulary or the option's own phrases. |
|
|
218
218
|
| input `invites:[names]`, `form.confirm.invites` | The other inputs this question (or the read-back) also asks for ("NEW CARD, LINK, or a different amount" invites `amount`), in its form or an enclosing one; read as in order while it is asked. |
|
|
219
219
|
| profile `out_of_order:{…}` | A narrower signature for out-of-order reading: the payment card takes a bare last four when asked, but out of order only a card word or "ending in 4242". |
|
|
220
|
+
| `authenticate-user` `other_whole_answer:true` | At the code question "another account" switches only as the whole answer; inside a sentence the reply ranker decides, offered `another_account` and `no_other_account` ("I understand. We still need to secure access to your account.", then the question). Default off. |
|
|
220
221
|
| input `confirm_out_of_order:{word,word_es,message,message_es}` | Out of order a number fills this input only beside its `word`, in either language. Alone, the caller hears `message` (`{value}` is the number, `{word}` the word) and the current question again, which counts as an attempt. `authenticate-user`'s `code_confirm` is the same declaration for a `code_validator`, whose flow reads it with `saysConfirmWord` and `confirmOutOfOrderLine`: an account number at the verification-code question, where it may be a code said with an extra digit. |
|
|
221
222
|
| input `choices_from` | The choice's values come from prepared data (the caller's saved cards), so its signature is those values. |
|
|
222
223
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jsfe",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.35.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",
|