jsfe 1.15.6 → 1.15.8
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/flows.js +3 -3
- package/dist/forms/identity-flows.d.ts.map +1 -1
- package/dist/forms/identity-flows.js +4 -1
- package/dist/helpers/match-inputs.d.ts.map +1 -1
- package/dist/helpers/match-inputs.js +14 -0
- package/dist/helpers/money.d.ts.map +1 -1
- package/dist/helpers/money.js +5 -2
- package/dist/host/intent-stages.d.ts +6 -1
- package/dist/host/intent-stages.d.ts.map +1 -1
- package/dist/host/intent-stages.js +8 -2
- package/dist/runtime-identity.js +1 -1
- package/package.json +1 -1
package/dist/forms/flows.js
CHANGED
|
@@ -521,8 +521,8 @@ const disambiguate = {
|
|
|
521
521
|
SET('di_build', 'di_choices', "readings.map(function (r, i) { var own = String(r.label + ' ' + r.label_es).toLowerCase().split(/\\s+/).filter(function (w) { return w.length > 3 && !readings.some(function (o, j) { return j !== i && String(o.label + ' ' + o.label_es).toLowerCase().split(/\\s+/).indexOf(w) > -1; }); }); return { key: 'r' + i, phrases: [di_keys[i], r.label, r.label_es].concat(own) }; })"),
|
|
522
522
|
SET('di_said', 'di_value', "readings[0].pii ? '' : String(readings[0].value)"),
|
|
523
523
|
// Options of ONE input (a phrase two choices share) are asked by name: "Do you mean A or B?". The value is a choice key, never words to say.
|
|
524
|
-
SET('di_question', 'di_q', "(readings.every(function (r) { return r.option === true; }) ? 'Do you mean ' + readings.map(function (r) { return r.label; }).join(' or ') : (di_value ? 'Is ' + di_value + ' your ' : 'Is that your ') + readings.map(function (r) { return r.label; }).join(' or your ')) + '? ' + readings.map(function (r, i) { return (cargo.voice ? 'Press ' + di_keys[i] + ' or say ' : '
|
|
525
|
-
SET('di_question_es', 'di_q_es', "(readings.every(function (r) { return r.option === true; }) ? '¿Se refiere a ' + readings.map(function (r) { return r.label_es; }).join(' o a ') : (di_value ? '¿' + di_value + ' es su ' : '¿Eso es su ') + readings.map(function (r) { return r.label_es; }).join(' o su ')) + '? ' + readings.map(function (r, i) { return (cargo.voice ? 'Presione ' + di_keys[i] + ' o diga ' : '
|
|
524
|
+
SET('di_question', 'di_q', "(readings.every(function (r) { return r.option === true; }) ? 'Do you mean ' + readings.map(function (r) { return r.label; }).join(' or ') : (di_value ? 'Is ' + di_value + ' your ' : 'Is that your ') + readings.map(function (r) { return r.label; }).join(' or your ')) + '? ' + readings.map(function (r, i) { return (cargo.voice ? 'Press ' + di_keys[i] + ' or say ' : (i ? String(cargo.verb || 'type') : String(cargo.verb || 'type').charAt(0).toUpperCase() + String(cargo.verb || 'type').slice(1)) + ' ') + r.label.toUpperCase(); }).join(', ') + '.'"),
|
|
525
|
+
SET('di_question_es', 'di_q_es', "(readings.every(function (r) { return r.option === true; }) ? '¿Se refiere a ' + readings.map(function (r) { return r.label_es; }).join(' o a ') : (di_value ? '¿' + di_value + ' es su ' : '¿Eso es su ') + readings.map(function (r) { return r.label_es; }).join(' o su ')) + '? ' + readings.map(function (r, i) { return (cargo.voice ? 'Presione ' + di_keys[i] + ' o diga ' : (i ? String(cargo.verb_es || 'escriba') : String(cargo.verb_es || 'escriba').charAt(0).toUpperCase() + String(cargo.verb_es || 'escriba').slice(1)) + ' ') + r.label_es.toUpperCase(); }).join(', ') + '.'"),
|
|
526
526
|
SET('di_owner', 'di_form', 'readings[0].form'),
|
|
527
527
|
SET('di_must_complete', 'di_must', 'readings.some(function(r){return formMustComplete(cargo,r.form,r.name);})'),
|
|
528
528
|
SET('di_counter', 'di_input', "'__ambiguity:'+readings.map(function(r){return r.form+':'+r.name;}).sort().join('|')"),
|
|
@@ -677,7 +677,7 @@ const PATH_JS = "(function (p) { var o = { cargo: cargo }; String(p || '').split
|
|
|
677
677
|
// all have values (the last one is the fallback). Formats: money, money_nonzero (a zero amount counts as no value), date,
|
|
678
678
|
// date_no_year, last4, digits (one by one on a call), text. A `say` may be an object keyed by the value's `state` field
|
|
679
679
|
// ({ all: …, some: …, none: … }) when the sentence itself changes with the data.
|
|
680
|
-
const RENDER_JS = "function fmt1(v, f, lang) { if (v === undefined || v === null || v === '') { return ''; } else if (f === 'money') { return amountToSpeech(v, lang, !!cargo.voice); } else if (f === 'money_nonzero') { return Number(v) === 0 ? '' : amountToSpeech(v, lang, !!cargo.voice); } else if (f === 'date') { return dateToWords(v, lang, true); } else if (f === 'date_no_year') { return dateToWords(v, lang, false); } else if (f === 'last4') { return last4(v, !!cargo.voice); } else if (f === 'digits') { return cargo.voice ? String(v).split('').join(', ') : String(v); } else { return String(v); } } function fill(t, obj, fmts, lang, extra) { var one = function (seg) { var gone = false; var out = String(seg).replace(/\\{(\\w+)\\}/g, function (m, k) { var v = Object.prototype.hasOwnProperty.call(extra, k) ? String(extra[k] || '') : fmt1(obj !== null && typeof obj === 'object' ? obj[k] : (k === 'value' ? obj : undefined), fmts && typeof fmts === 'object' ? fmts[k] : fmts, lang); if (v === '') { gone = true; } else { /* filled */ } return v; }); return { out: out, gone: gone }; }; var t2 = String(t).replace(/\\[([^\\]]*)\\]/g, function (m, seg) { var alts = seg.split('|'); for (var i = 0; i < alts.length; i++) { var r = one(alts[i]); if (!r.gone || i === alts.length - 1) { return r.gone && alts.length === 1 ? '' : r.out; } else { /* try the next alternative */ } } return ''; }); return one(t2).out.replace(
|
|
680
|
+
const RENDER_JS = "function fmt1(v, f, lang) { if (v === undefined || v === null || v === '') { return ''; } else if (f === 'money') { return amountToSpeech(v, lang, !!cargo.voice); } else if (f === 'money_nonzero') { return Number(v) === 0 ? '' : amountToSpeech(v, lang, !!cargo.voice); } else if (f === 'date') { return dateToWords(v, lang, true); } else if (f === 'date_no_year') { return dateToWords(v, lang, false); } else if (f === 'last4') { return last4(v, !!cargo.voice); } else if (f === 'digits') { return cargo.voice ? String(v).split('').join(', ') : String(v); } else { return String(v); } } function fill(t, obj, fmts, lang, extra) { var one = function (seg) { var gone = false; var out = String(seg).replace(/\\{(\\w+)\\}/g, function (m, k) { var v = Object.prototype.hasOwnProperty.call(extra, k) ? String(extra[k] || '') : fmt1(obj !== null && typeof obj === 'object' ? obj[k] : (k === 'value' ? obj : undefined), fmts && typeof fmts === 'object' ? fmts[k] : fmts, lang); if (v === '') { gone = true; } else { /* filled */ } return v; }); return { out: out, gone: gone }; }; var t2 = String(t).replace(/\\[([^\\]]*)\\]/g, function (m, seg) { var alts = seg.split('|'); for (var i = 0; i < alts.length; i++) { var r = one(alts[i]); if (!r.gone || i === alts.length - 1) { return r.gone && alts.length === 1 ? '' : r.out; } else { /* try the next alternative */ } } return ''; }); return one(t2).out.replace(/[ \\t]+([.,;:])/g, '$1').replace(/[ \\t]{2,}/g, ' ').trim(); } function empty(v) { return v === undefined || v === null || v === '' || (Array.isArray(v) && v.length === 0) || (v !== null && typeof v === 'object' && !Array.isArray(v) && Object.keys(v).every(function (k) { return v[k] === undefined || v[k] === null || v[k] === ''; })); } function render(f, src, lang, name) { var es = lang === 'es'; var o = src; String(f.path).split('.').forEach(function (k) { o = o === undefined || o === null ? undefined : o[k]; }); var label = es ? (f.label_es || f.label) : f.label; var extra = { name: name, label: label }; if (empty(o)) { var miss = es ? (f.missing_es || f.missing) : f.missing; return miss ? fill(miss, {}, null, lang, extra) : (es ? 'No veo ' + label + ' en su ' + (provides.subject_es || provides.subject || 'cuenta') + '.' : (\"I don't see a \" + label + ' on your ' + (provides.subject || 'account') + '.')); } else if (f.format && typeof f.format === 'object' && f.format.type === 'list' && Array.isArray(o)) { var it = es ? (f.format.item_es || f.format.item) : f.format.item; extra.count = String(o.length); extra.items = o.map(function (x) { return fill(it, x, f.format.fields, lang, { name: name }); }).join(es ? (f.format.join_es || f.format.join || '; ') : (f.format.join || '; ')); var lt = o.length === 1 ? (es ? (f.say_one_es || f.say_one || f.say_es || f.say) : (f.say_one || f.say)) : (es ? (f.say_es || f.say) : f.say); return fill(lt || '{items}', o, null, lang, extra); } else { var t = es ? (f.say_es || f.say) : f.say; if (t && typeof t === 'object') { t = t[o && o.state] || t.default || ''; } else { /* one sentence */ } return fill(t || (es ? 'Su {label} es {value}.' : 'Your {label} is {value}.'), o, f.format, lang, extra); } }";
|
|
681
681
|
const NAME_JS = "(function (p) { var o = { cargo: cargo }; String(p || 'cargo.displayName').split('.').forEach(function (k) { o = o === undefined || o === null ? undefined : o[k]; }); var n = String(o || '').trim(); return n && n === n.toUpperCase() ? n.toLowerCase().replace(/(^|[\\s'-])([a-záéíóúñ])/g, function (m, a, c) { return a + c.toUpperCase(); }) : n; })(provides.name_path)";
|
|
682
682
|
/** Schedule deferred catalogs once authorized or once the existing task gate ends; each still enforces its own gate. */
|
|
683
683
|
export function takeReadyProviderRequests(cargo, form) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"identity-flows.d.ts","sourceRoot":"","sources":["../../src/forms/identity-flows.js"],"names":[],"mappings":"AA2BA,+GAA+G;AAC/G,oFAOC;AAQD,wFAAwF;AACxF,iDAGC;AACD,6CAMC;AACD,qHAQC;AAMD,4EAKC;AAoFD,4DAKC;
|
|
1
|
+
{"version":3,"file":"identity-flows.d.ts","sourceRoot":"","sources":["../../src/forms/identity-flows.js"],"names":[],"mappings":"AA2BA,+GAA+G;AAC/G,oFAOC;AAQD,wFAAwF;AACxF,iDAGC;AACD,6CAMC;AACD,qHAQC;AAMD,4EAKC;AAoFD,4DAKC;AAmHD,8EAGC;AA4BD,4FAA4F;AAC5F;;;;EAOC;AAQD,0BAAmI"}
|
|
@@ -280,8 +280,11 @@ for (const flow of [auth, validateCode]) {
|
|
|
280
280
|
flow.steps.unshift(...otpMenuMessages.map(([name, value]) => set(name, name, value.includes('{{') ? value : JSON.stringify(value))));
|
|
281
281
|
}
|
|
282
282
|
const retryParams = [param('error_message', 'Failure sentence'), param('error_message_es', 'Spanish failure sentence'), param('retry_flow', 'Retry destination'), param('cancel_flow', 'Exit destination'), param('capture_patterns', 'Declared contact capture readers', 'array'), param('retry_call_type', 'reboot or replace'), param('allow_code_entry', 'Accept the active six-digit challenge', 'boolean'), param('question', 'Retry question'), param('question_es', 'Spanish retry question'), param('caller_outcome', 'Unresolved outcome'), param('caller_reason', 'Unresolved reason'), param('parent_form', 'Owning task FORM instance'), param('contact_options', 'Contact field protocol declaration', 'object')];
|
|
283
|
-
const retry = worker('generic-retry-with-options', 'GenericRetryWithOptions', 'Collect a retry, alternate contact, code or exit through the shared FORM adapter.', { scope: 'isolated', parameters: retryParams, variables: vars({ error_message: '', error_message_es: '', retry_flow: 'authenticate-user', cancel_flow: 'contact-support', capture_patterns: [], retry_call_type: 'reboot', allow_code_entry: false, question: '', question_es: '', caller_outcome: '', caller_reason: '', parent_form: '', contact_options: null, user_choice: '', smart_capture_result: null, normalized_otp_code: '', retry_question: '', retry_question_es: '', retry_yes_words: ['phone', 'email', 'cell', 'mobile', 'teléfono', 'telefono', 'celular', 'móvil', 'movil', 'correo electrónico', 'correo', 'try again', 'try it again', 'retry', 'intentar', 'intentarlo', 'intentar de nuevo', 'intentar nuevamente', 'intentarlo de nuevo', 'intentarlo otra vez', 'de nuevo', 'nuevamente', 'otra vez', 'reintentar', 'volver a intentar', 'volver a intentarlo'],
|
|
283
|
+
const retry = worker('generic-retry-with-options', 'GenericRetryWithOptions', 'Collect a retry, alternate contact, code or exit through the shared FORM adapter.', { scope: 'isolated', parameters: retryParams, variables: vars({ error_message: '', error_message_es: '', retry_flow: 'authenticate-user', cancel_flow: 'contact-support', capture_patterns: [], retry_call_type: 'reboot', allow_code_entry: false, question: '', question_es: '', caller_outcome: '', caller_reason: '', parent_form: '', contact_options: null, user_choice: '', smart_capture_result: null, normalized_otp_code: '', retry_question: '', retry_question_es: '', retry_yes_words: ['phone', 'email', 'cell', 'mobile', 'teléfono', 'telefono', 'celular', 'móvil', 'movil', 'correo electrónico', 'correo', 'try again', 'try it again', 'retry', 'intentar', 'intentarlo', 'intentar de nuevo', 'intentar nuevamente', 'intentarlo de nuevo', 'intentarlo otra vez', 'de nuevo', 'nuevamente', 'otra vez', 'reintentar', 'volver a intentar', 'volver a intentarlo'], retry_otp_profiles: OTP_PROFILES, retry_profiles_base: [{ name: 'phone', label: 'phone number', label_es: 'número de teléfono', profile: { kind: 'phone' } }, { name: 'email_address', label: 'email address', label_es: 'correo electrónico', profile: { kind: 'email' } }], retry_profiles: [] }), contract: { role: 'collector', summary: 'Common bounded service retry menu' }, steps: [
|
|
284
284
|
set('retry_parent', 'parent_form', "parent_form || cargo.authOptions && cargo.authOptions.parent_form || cargo.contactOptions && cargo.contactOptions.parent_form || ''"),
|
|
285
|
+
// The menu answers what its capture declares: an account number in the tenant's pattern (get-cell-or-email's
|
|
286
|
+
// account_pattern, carried in cargo.authCapture) is the menu's own answer, never the parent's, so it reaches the lookup.
|
|
287
|
+
set('retry_profiles', 'retry_profiles', "retry_profiles_base.concat((Array.isArray(capture_patterns) ? capture_patterns : []).filter(function (c) {return c && c.variable === 'acct_number' && c.regex;}).slice(0,1).map(function (c) {return {name:'account',label:'account number',label_es:'número de cuenta',profile:{kind:'identifier',pattern:c.regex}};}),retry_otp_profiles)"),
|
|
285
288
|
set('retry_question', 'retry_question', "error_message+' '+(question || 'Would you like to try again?')+' To retry '+(cargo.voice?'Press 1 or ':'')+cargo.verb+' YES. '+(callerTextNumber(cargo)?'To switch to text press 9 or '+cargo.verb+' TEXT. ':'')+'To abort '+(cargo.voice?'press the asterisk key or ':'')+cargo.verb+' EXIT.'"),
|
|
286
289
|
set('retry_question_es', 'retry_question_es', "error_message_es+' '+(question_es || '¿Le gustaría intentar nuevamente?')+' Para reintentar '+(cargo.voice?'Presione 1 o ':'')+cargo.verb_es+' SÍ. '+(callerTextNumber(cargo)?'Para cambiar a texto presione 9 o '+cargo.verb_es+' TEXTO. ':'')+'Para salir '+(cargo.voice?'presione la tecla de asterisco o ':'')+cargo.verb_es+' SALIR.'"),
|
|
287
290
|
call('retry_collect', 'collect-service-answer', { profiles: '{{retry_profiles}}', question: '{{retry_question}}', question_es: '{{retry_question_es}}', keypad: { min: 1, max: 1 }, parent_form: '{{parent_form}}' }),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"match-inputs.d.ts","sourceRoot":"","sources":["../../src/helpers/match-inputs.js"],"names":[],"mappings":"AAiBA;;;;GAIG;AACH,sEAQC;AAqED;;;GAGG;AACH,sFAwGC;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH;mHACmH;AACnH;;;;;;;;;;;;;;;;;;;;;EAeC;AACD;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"match-inputs.d.ts","sourceRoot":"","sources":["../../src/helpers/match-inputs.js"],"names":[],"mappings":"AAiBA;;;;GAIG;AACH,sEAQC;AAqED;;;GAGG;AACH,sFAwGC;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH;mHACmH;AACnH;;;;;;;;;;;;;;;;;;;;;EAeC;AACD;;;;;;;;;;;;;;;;;;;;;;EAiNC"}
|
|
@@ -388,6 +388,20 @@ export function matchInputs(answer, candidates, current, choiceKey) {
|
|
|
388
388
|
return true;
|
|
389
389
|
}
|
|
390
390
|
});
|
|
391
|
+
// Rule 4 for a compound answer: a bare number beside other words ("New card 100") is the invited amount even when the
|
|
392
|
+
// amount reader, which reads only a whole monetary answer, could not read it from the whole answer.
|
|
393
|
+
for (const name of invitedNames) {
|
|
394
|
+
const field = list.find((c) => c.name === name && c.profile.kind === 'amount' && !isCur(c));
|
|
395
|
+
const other = field && !readings.some((reading) => reading.form === field.form && reading.name === name) ? readings.find((reading) => reading.rule === 'out_of_order' && bare(reading)) : null;
|
|
396
|
+
const read = other ? readInputProfile(field.profile, spanText(other), true) : null;
|
|
397
|
+
if (read && !read.alternatives && !read.incompleteMoney) {
|
|
398
|
+
readings.push({ ...other, form: field.form, name: field.name, value: read.value, known: false, repeatedChoice: false, wholeChoice: false, choice: false, narrative: false, resetOn: [], keypad: false,
|
|
399
|
+
group: field.group, exclusive: field.exclusive === true, level: forms.indexOf(field.scope || field.form), current: false, confirm: Array.isArray(field.confirm_against) ? field.confirm_against : [], rule: 'invited' });
|
|
400
|
+
}
|
|
401
|
+
else {
|
|
402
|
+
// no invited amount, or it was read, or no bare number for it
|
|
403
|
+
}
|
|
404
|
+
}
|
|
391
405
|
// Rule 4: a single digit the current question offers as a key is that key (the keypad reading wins its group below);
|
|
392
406
|
// one it does not offer reaches the invited amount, whose own validation decides ("3 dollars?").
|
|
393
407
|
const paired = (a, b) => a.confirm.indexOf(b.name) > -1 || b.confirm.indexOf(a.name) > -1;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"money.d.ts","sourceRoot":"","sources":["../../src/helpers/money.js"],"names":[],"mappings":"AAIA,gHAAgH;AAChH,wDAGC;
|
|
1
|
+
{"version":3,"file":"money.d.ts","sourceRoot":"","sources":["../../src/helpers/money.js"],"names":[],"mappings":"AAIA,gHAAgH;AAChH,wDAGC;AAqBD;8FAC8F;AAC9F,oEAIC;AACD,+FAA+F;AAC/F,0DAEC;AACD;oGACoG;AACpG,4DAGC;AAWD;;;GAGG;AACH,+DAYC;AACD,4FAA4F;AAC5F,iEAAsG"}
|
package/dist/helpers/money.js
CHANGED
|
@@ -10,8 +10,11 @@ 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
|
|
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;
|
|
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
|
+
// The field named with its value ("100 amount", "the amount is 100", "monto 100"): the amount, said as such.
|
|
16
|
+
const AMOUNT_LABEL_PREFIX = /^(?:(?:the|el|la)\s+)?(?:amount|monto|cantidad)(?:\s+(?:is|of|es|de))?[:\s]+(?=\$|\d)/i;
|
|
17
|
+
const AMOUNT_LABEL_SUFFIX = /(?<=\d|\$)\s+(?:(?:as\s+)?(?:the\s+)?amount|(?:de\s+|el\s+)?monto|(?:de\s+|la\s+)?cantidad)$/i;
|
|
15
18
|
const ACCOUNT_PREFIX = /^(?:(?:sub[ -]?account|subcuenta|cuenta)\s+\d{1,8}\s*,\s*)/i;
|
|
16
19
|
const DIGIT_WORD = '(?:zero|one|two|three|four|five|six|seven|eight|nine|cero|uno|dos|tres|cuatro|cinco|seis|siete|ocho|nueve)';
|
|
17
20
|
const CARD_SUFFIX = new RegExp('\\s+(?:with (?:the |my )?card(?: (?:ending(?: in)?|number))?|con (?:la |mi )?tarjeta(?: (?:que termina en|terminad[ao] en))?)\\s+(?:\\d(?:[ -]?\\d){3}|' + DIGIT_WORD + '(?: ' + DIGIT_WORD + '){3})$', 'i');
|
|
@@ -19,7 +22,7 @@ const ACCOUNT_SUFFIX = /\s+(?:(?:on|to) (?:sub[ -]?account|account)|(?:a|en) (?:
|
|
|
19
22
|
function completeMoneyAnswer(input) {
|
|
20
23
|
let raw = String(input ?? '').replace(/\\(['"\\])/g, '$1').trim().replace(/^[¡!]+|[.!]+$/g, '').trim();
|
|
21
24
|
raw = raw.replace(/^(?:please|por favor)[, ]+/i, '').replace(/[, ]+(?:please|por favor)$/i, '').trim();
|
|
22
|
-
raw = raw.replace(PAYMENT_PREFIX, '').replace(ACCOUNT_PREFIX, '').replace(CARD_SUFFIX, '').replace(ACCOUNT_SUFFIX, '').trim();
|
|
25
|
+
raw = raw.replace(PAYMENT_PREFIX, '').replace(AMOUNT_LABEL_PREFIX, '').replace(AMOUNT_LABEL_SUFFIX, '').replace(ACCOUNT_PREFIX, '').replace(CARD_SUFFIX, '').replace(ACCOUNT_SUFFIX, '').trim();
|
|
23
26
|
if (NUMERIC_MONEY.test(raw))
|
|
24
27
|
return true;
|
|
25
28
|
const { readings, invalid } = scanSpokenMoney(raw);
|
|
@@ -23,6 +23,9 @@
|
|
|
23
23
|
import type { FlowParameterDefinition } from '../index.js';
|
|
24
24
|
/** A noul at or above this is a yes. */
|
|
25
25
|
export declare const STAGE_YES_THRESHOLD = 0.5;
|
|
26
|
+
/** Stage 1: a start below this (`any`) whose route gives its top flow less than SPLIT_ROUTE_SHARE is "None" (owner 2026-10-29). */
|
|
27
|
+
export declare const WEAK_START = 0.55;
|
|
28
|
+
export declare const SPLIT_ROUTE_SHARE = 0.7;
|
|
26
29
|
export interface StageTurn {
|
|
27
30
|
role: 'user' | 'assistant';
|
|
28
31
|
content: string;
|
|
@@ -112,7 +115,9 @@ export declare function decodeFlowLlm(text: string, stage: FlowStageRequest): {
|
|
|
112
115
|
};
|
|
113
116
|
/**
|
|
114
117
|
* System One's stage-1 answers as the canonical reply. `any` at or above the threshold starts the flow `route`
|
|
115
|
-
* chose; below it, "None". `
|
|
118
|
+
* chose; below it, "None". A weak start (`any` below WEAK_START) is "None" too when the route is split, its top flow
|
|
119
|
+
* under SPLIT_ROUTE_SHARE: unsure a flow is wanted and unsure which, the Guru answers (prod 2026-10-29: 'Esta
|
|
120
|
+
* cancelación afecta mi crédito?' after a cancellation, any 0.51, restarted CancelService). `scores` are the top three flows by the chance that each is the one started,
|
|
116
121
|
* any × its route probability (for logs and the host's near-miss hedge); `any` is logged with them. Throws when
|
|
117
122
|
* either answer is missing or names no offered flow.
|
|
118
123
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"intent-stages.d.ts","sourceRoot":"","sources":["../../src/host/intent-stages.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAE3D,wCAAwC;AACxC,eAAO,MAAM,mBAAmB,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"intent-stages.d.ts","sourceRoot":"","sources":["../../src/host/intent-stages.ts"],"names":[],"mappings":"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"}
|
|
@@ -22,6 +22,9 @@
|
|
|
22
22
|
*/
|
|
23
23
|
/** A noul at or above this is a yes. */
|
|
24
24
|
export const STAGE_YES_THRESHOLD = 0.5;
|
|
25
|
+
/** Stage 1: a start below this (`any`) whose route gives its top flow less than SPLIT_ROUTE_SHARE is "None" (owner 2026-10-29). */
|
|
26
|
+
export const WEAK_START = 0.55;
|
|
27
|
+
export const SPLIT_ROUTE_SHARE = 0.7;
|
|
25
28
|
// ── the rules: written once, rendered for both executors ─────────────────────────────────────────────
|
|
26
29
|
const FLOW_TRUE = "The latest message, read with the conversation, is what the workflow's description says should start it: the user asks for its action or information, or raises the problem it exists to handle. Also when it accepts the assistant's offer in the conversation to start or resume it (assistant: \"Shall I try the payment again?\" → user: \"yes\").";
|
|
27
30
|
const FLOW_FALSE = "It only shares a topic with the workflow, is not what the workflow's description covers, or is a greeting, an acknowledgement or a fragment that asks for nothing.";
|
|
@@ -110,7 +113,9 @@ export function decodeFlowLlm(text, stage) {
|
|
|
110
113
|
}
|
|
111
114
|
/**
|
|
112
115
|
* System One's stage-1 answers as the canonical reply. `any` at or above the threshold starts the flow `route`
|
|
113
|
-
* chose; below it, "None". `
|
|
116
|
+
* chose; below it, "None". A weak start (`any` below WEAK_START) is "None" too when the route is split, its top flow
|
|
117
|
+
* under SPLIT_ROUTE_SHARE: unsure a flow is wanted and unsure which, the Guru answers (prod 2026-10-29: 'Esta
|
|
118
|
+
* cancelación afecta mi crédito?' after a cancellation, any 0.51, restarted CancelService). `scores` are the top three flows by the chance that each is the one started,
|
|
114
119
|
* any × its route probability (for logs and the host's near-miss hedge); `any` is logged with them. Throws when
|
|
115
120
|
* either answer is missing or names no offered flow.
|
|
116
121
|
*/
|
|
@@ -128,7 +133,8 @@ export function decodeFlowSystemOne(answers, stage, { threshold = STAGE_YES_THRE
|
|
|
128
133
|
const probabilities = Object.fromEntries(names.map(n => [n, Number(raw[n]) || 0]));
|
|
129
134
|
const ranked = names.map(n => ({ name: n, p: any * probabilities[n] })).sort((a, b) => b.p - a.p);
|
|
130
135
|
const scores = ranked.slice(0, 3);
|
|
131
|
-
const
|
|
136
|
+
const split = any < WEAK_START && !!ranked[0] && probabilities[ranked[0].name] < SPLIT_ROUTE_SHARE;
|
|
137
|
+
const flowName = any >= threshold && ranked[0] && !split ? ranked[0].name : 'None';
|
|
132
138
|
return { flowName, scores, any };
|
|
133
139
|
}
|
|
134
140
|
/**
|
package/dist/runtime-identity.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const JSFE_RUNTIME_FINGERPRINT = "sha256:
|
|
1
|
+
export const JSFE_RUNTIME_FINGERPRINT = "sha256:b881e2d360ed9e56f57e293c74fd0fabc93d1cc68d8ce1b4aa5e09511e843cee";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jsfe",
|
|
3
|
-
"version": "1.15.
|
|
3
|
+
"version": "1.15.8",
|
|
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",
|