jsfe 1.11.1 → 1.12.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.
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"flows.d.ts","sourceRoot":"","sources":["../../src/forms/flows.js"],"names":[],"mappings":"AA4HA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA4DC;AACD;;;GAGG;AACH;;;;;;EAGC;
|
|
1
|
+
{"version":3,"file":"flows.d.ts","sourceRoot":"","sources":["../../src/forms/flows.js"],"names":[],"mappings":"AA4HA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA4DC;AACD;;;GAGG;AACH;;;;;;EAGC;AAgcD,wHAAwH;AACxH,wEAaC;AACD,4FAA4F;AAC5F;;;EAYC;AACD,sGAAsG;AACtG;;;EAOC;AA3nBM,6CAAuG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmzB9G;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAOG"}
|
package/dist/forms/flows.js
CHANGED
|
@@ -411,6 +411,17 @@ const readFormChoice = {
|
|
|
411
411
|
SIDE('choice_clear', 'clearFormMisses(cargo,form,input)'),
|
|
412
412
|
FLOW('choice_accept', 'collect-input-accept', 'call', { hits: '{{reading.hits}}' })]
|
|
413
413
|
};
|
|
414
|
+
// A confirmation's unread answer goes to the reply ranker with the confirmation question (owner 2026-10-27), never
|
|
415
|
+
// straight to the host. The ranker is offered NO only: a confirmation is accepted only by its exact YES. Result:
|
|
416
|
+
// cargo.confirmRanked = 'no' | 'none' (ConfirmForm reads it); `abort` ends every flow; `new_request` goes to intent detection.
|
|
417
|
+
const readConfirmReply = {
|
|
418
|
+
id: 'read-confirm-reply', name: 'ReadConfirmReply', version: '1.0.0', scope: 'isolated', description: 'Rank an unreadable answer to a confirmation: it declines (NO) or is asked again; a withdrawal or a new request leaves with saved form progress. Never confirms.',
|
|
419
|
+
parameters: [{ name: 'form', type: 'string' }, { name: 'question', type: 'string' }, { name: 'question_es', type: 'string' }],
|
|
420
|
+
variables: vars({ question: '', question_es: '', asked: '', reply: null }),
|
|
421
|
+
steps: [SET('confirm_asked', 'asked', "language === 'es' && question_es ? question_es : question"),
|
|
422
|
+
{ ...yieldDispatch('confirm_dispatch', 'form', "'confirmation_not_matched'"), onResponse: 'continue', responseContext: 'Current question: {{asked}}', responseParameters: [{ name: 'key', type: 'string', description: "'no' when the reply declines or refuses what the question asks to confirm. Leave it empty otherwise, including when it agrees." }], responseVariable: 'reply' },
|
|
423
|
+
SIDE('confirm_ranked', "(cargo.confirmRanked=String(reply && reply.key || '').trim().toLowerCase()==='no' ? 'no' : 'none',true)")]
|
|
424
|
+
};
|
|
414
425
|
const collectContextualAnswer = formFlow({ id: 'collect-contextual-answer', name: 'CollectContextualAnswer', description: 'Collect one open-ended answer using contextual DISPATCH and preserve the caller wording.',
|
|
415
426
|
parameters: [...['question', 'question_es', 'description', 'context', 'context_es'].map(name => ({ name, type: 'string' })), { name: 'choices', type: 'array' }, { name: 'digits', type: 'object' }],
|
|
416
427
|
extraVars: { question: '', question_es: '', description: '', context: 'your request', context_es: 'su solicitud', choices: [], digits: null },
|
|
@@ -495,11 +506,11 @@ const disambiguate = {
|
|
|
495
506
|
// ── confirm-form: the exact confirmation of a finished form; a correction is matched against the form chain ───────
|
|
496
507
|
const confirmForm = {
|
|
497
508
|
id: 'confirm-form', name: 'ConfirmForm', version: '1.0.0', scope: 'isolated',
|
|
498
|
-
description: "Worker: ask a form's confirmation (exact YES / NO; keys 1 and 2). A correction instead ('no, make it $40') is read against the form chain and stored, and the result is 'changed' so the form runs again; an unclear short answer is asked again, a long
|
|
509
|
+
description: "Worker: ask a form's confirmation (exact YES / NO; keys 1 and 2). A correction instead ('no, make it $40') is read against the form chain and stored, and the result is 'changed' so the form runs again; an unclear short answer is asked again, a long unread one goes to the reply ranker (read-confirm-reply). Result: cargo.formConfirm[form] = 'yes' | 'no' | 'changed'. Params: form, question(_es), exit_context(_es).",
|
|
499
510
|
parameters: ['form', 'question', 'question_es', 'exit_context', 'exit_context_es', 'phrases', 'words', 'interrupts', 'no_keys', 'clarifications', 'invites'].map((n) => ({ name: n, type: ['phrases', 'words', 'interrupts', 'no_keys', 'clarifications', 'invites'].includes(n) ? 'array' : 'string', description: n })).concat([{ name: 'named_fields', type: 'boolean', description: "true: an answer that names one of the form's inputs (its names) without a value re-asks only that input" }]),
|
|
500
511
|
variables: vars({ named_fields: false, cf_must: false, cf_named: null, cf_presets: FIELD_PRESETS, cf_chain: [], cf_m: null, cf_current: null, cf_result: '', cf_prompt: '', cf_prompt_es: '', cf_words: 0, cf_tries: 0, phrases: null, words: [], clarifications: [], no_keys: ['2'], interrupts: [], invites: [], cf_choices: [], cf_event: '', cf_amount: false, cf_questions: [], cf_provides: null, cf_skip_gate: '' }),
|
|
501
512
|
steps: [
|
|
502
|
-
SIDE('cf_reset', "(cargo.formConfirm = cargo.formConfirm || {}, cargo.formConfirm[form] = '', true)"),
|
|
513
|
+
SIDE('cf_reset', "(cargo.formConfirm = cargo.formConfirm || {}, cargo.formConfirm[form] = '', cargo.confirmRanked = '', true)"),
|
|
503
514
|
SET('cf_choices', 'cf_choices', `[{key:'yes',phrases:['1'].concat(Array.isArray(phrases)?phrases:Array.isArray(words)?words:[]),exact:true},{key:'no',phrases:(Array.isArray(no_keys)?no_keys:['2']).concat(${JSON.stringify(ANSWER_WORDS.no)}),exact:true}].concat((Array.isArray(interrupts)?interrupts:[]).map(function(event){return {key:'event:'+event.key,phrases:event.phrases ?? event.words,exact:event.exact!==false};}))`),
|
|
504
515
|
SET('cf_initial_chain', 'cf_chain', CHAIN_JS),
|
|
505
516
|
SET('cf_amount_context', 'cf_amount', "cf_chain.some(function(input){return input.profile && input.profile.kind==='amount';})"),
|
|
@@ -534,9 +545,11 @@ const confirmForm = {
|
|
|
534
545
|
'condition: cf_named': SET('cf_named_reset', 'cf_result', "(resetFormInputs(cargo, cf_named.form, [cf_named.name]), 'changed')"),
|
|
535
546
|
"condition: cargo.choice === 'no'": SET('cf_declined', 'cf_result', "'no'"),
|
|
536
547
|
"condition: Array.isArray(clarifications) && pickChoice(String(cargo.choiceText || '').replace(/[?¿]/g,''),[{key:'clarify',phrases:clarifications,exact:true}],false)==='clarify'": SET('cf_clarification', 'cf_result', "'clarification'"),
|
|
537
|
-
'condition: !cf_must && (cf_words >= 3 || cf_amount && cf_words > 0)':
|
|
548
|
+
'condition: !cf_must && (cf_words >= 3 || cf_amount && cf_words > 0)': FLOW('cf_elsewhere', 'read-confirm-reply', 'call', { form: '{{form}}', question: '{{cf_prompt}}', question_es: '{{cf_prompt_es}}' }),
|
|
538
549
|
default: SET('cf_unclear', 'cf_result', "'unclear'")
|
|
539
550
|
} },
|
|
551
|
+
// the ranker's reading of an unread answer: a decline is the NO; an answer that declines nothing is asked again
|
|
552
|
+
SET('cf_ranked', 'cf_result', "cf_result ? cf_result : cargo.confirmRanked === 'no' ? 'no' : cargo.confirmRanked === 'none' ? 'unclear' : ''"),
|
|
540
553
|
SET('cf_changed', 'cf_result', "cf_result ? cf_result : 'changed'"),
|
|
541
554
|
// an unclear short answer is asked again (the form runs again and reaches this question), at most twice
|
|
542
555
|
SET('cf_tries_count', 'cf_tries', "cf_result === 'unclear' ? missFormInput(cargo,form,'__confirm') : (clearFormMisses(cargo,form,'__confirm'),0)"),
|
|
@@ -826,7 +839,7 @@ const resumeForm = { id: 'resume-form', name: 'ResumeForm', version: '1.0.0', sc
|
|
|
826
839
|
SET('resume_definition', 'target', 'cargo.forms[form].definition'), SET('resume_parent', 'parent', "cargo.forms[form].parent || ''"), SET('resume_slot', 'slot', "cargo.forms[form].result_input || ''"),
|
|
827
840
|
FLOW('resume_owned_form', '{{target}}', 'replace', { form_instance: '{{form}}', parent_form: '{{parent}}', result_input: '{{slot}}' })
|
|
828
841
|
] };
|
|
829
|
-
const LIBRARY_FLOWS = [readFormReply, readFormChoice, confirmInputProposal, proposalDeclined, resumeForm, performAction, sendTextAction, prepareInput, runForm, collectInput, validateBatch, requestCancelled, readFormResponse, collectContextualAnswer, accept, acceptedEffects, disambiguate, confirmForm, formDone, providerMenu, ...validators, ...fieldCollectors, ...subForms, provideData, provideQueuedData, loadSource, answerFacts];
|
|
842
|
+
const LIBRARY_FLOWS = [readFormReply, readFormChoice, readConfirmReply, confirmInputProposal, proposalDeclined, resumeForm, performAction, sendTextAction, prepareInput, runForm, collectInput, validateBatch, requestCancelled, readFormResponse, collectContextualAnswer, accept, acceptedEffects, disambiguate, confirmForm, formDone, providerMenu, ...validators, ...fieldCollectors, ...subForms, provideData, provideQueuedData, loadSource, answerFacts];
|
|
830
843
|
const LIBRARY_CONTRACTS = {
|
|
831
844
|
'confirm-input-proposal': { role: 'collector', requires: [INPUT_PROPOSALS_REQUIREMENT] }, 'confirm-input-proposal-declined': { role: 'worker', requires: [INPUT_PROPOSALS_REQUIREMENT] },
|
|
832
845
|
'resume-form': { role: 'worker' },
|
|
@@ -835,7 +848,7 @@ const LIBRARY_CONTRACTS = {
|
|
|
835
848
|
'prepare-input': { role: 'worker' }, 'run-form': { role: 'worker' }, 'collect-input': { role: 'worker' },
|
|
836
849
|
'request-cancelled': { role: 'worker' },
|
|
837
850
|
'read-form-response': { role: 'worker', requires: [CONTEXTUAL_RESPONSES_REQUIREMENT] },
|
|
838
|
-
'read-form-reply': { role: 'worker' }, 'read-form-choice': { role: 'worker', requires: [CONTEXTUAL_RESPONSES_REQUIREMENT] },
|
|
851
|
+
'read-form-reply': { role: 'worker' }, 'read-form-choice': { role: 'worker', requires: [CONTEXTUAL_RESPONSES_REQUIREMENT] }, 'read-confirm-reply': { role: 'worker', requires: [CONTEXTUAL_RESPONSES_REQUIREMENT] },
|
|
839
852
|
'collect-contextual-answer': { role: 'collector', requires: [CONTEXTUAL_RESPONSES_REQUIREMENT] },
|
|
840
853
|
'validate-input-batch': { role: 'worker', result: { path: 'cargo.inputBatch', type: 'object' } },
|
|
841
854
|
'collect-input-accept': { role: 'worker' }, 'input-accepted-effects': { role: 'worker' },
|
package/dist/runtime-identity.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const JSFE_RUNTIME_FINGERPRINT = "sha256:
|
|
1
|
+
export const JSFE_RUNTIME_FINGERPRINT = "sha256:d2fe25c463500cc6a524418c9ccb5ac710130ac6cce764894ca522f6fe83a22c";
|
|
@@ -242,3 +242,13 @@ counted as an attempt, so `max_attempts` still bounds it; `abort` ends every flo
|
|
|
242
242
|
detection. A host fixture that answered such turns with intent detection only must now also answer `rank_reply`.
|
|
243
243
|
|
|
244
244
|
**A press verb before a key picks it:** "ya oprimí el uno", "I already pressed one", "marqué el 2" (not after a negation).
|
|
245
|
+
|
|
246
|
+
## 1.12.0 — a confirmation's unread answer goes to the reply ranker
|
|
247
|
+
|
|
248
|
+
ConfirmForm no longer hands a long unread answer (three or more words, or any words when the form holds an amount) to the
|
|
249
|
+
host with a plain DISPATCH. The new shared worker `read-confirm-reply` ranks it against the confirmation question:
|
|
250
|
+
`answers` with the reply declining (stage-2 `key` = `no`) is the confirmation's NO, so the form's decline path runs;
|
|
251
|
+
`answers` with nothing declined asks the confirmation again, counted as a miss (two misses still hand off, as before);
|
|
252
|
+
`abort` ends every flow; `new_request` goes to intent detection. The ranker is offered NO only: a confirmation is accepted
|
|
253
|
+
only by its exact YES, never by a ranked reading. Hosts that ship the shared workers in a system set must add
|
|
254
|
+
`read-confirm-reply`; a host fixture that answered such turns with intent detection only must now also answer `rank_reply`.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jsfe",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.12.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",
|