fix(rectification): keep single-event opening on the existing collect path (BUG-604)
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -30,6 +30,15 @@ export type CollectDomain =
|
||||
|
||||
export const GENERIC_COLLECT_QUESTION = "先说你最容易想起的一两件,年月大概就行。";
|
||||
|
||||
/** One-time suffix on the first dated-collect stem. Not a chase-more turn. */
|
||||
export const FIRST_DATED_COLLECT_INVITE = "想到别的也可以一起说。";
|
||||
|
||||
export function withFirstDatedCollectInvite(stem: string): string {
|
||||
const spoken = stem.trim();
|
||||
if (!spoken || spoken.includes(FIRST_DATED_COLLECT_INVITE)) return spoken;
|
||||
return `${spoken}${FIRST_DATED_COLLECT_INVITE}`;
|
||||
}
|
||||
|
||||
/** Six everyday domains named in the opening body. Not years. */
|
||||
export const OPENING_COLLECT_DOMAINS = [
|
||||
"升学",
|
||||
@@ -108,6 +117,7 @@ export const RECTIFICATION_USER_COPY = {
|
||||
collectHandoff: "接下来我们继续。",
|
||||
collectDeclinedAck: "记下了,这方面先跳过。",
|
||||
collectSkippedAck: "记下了,这题先放着。",
|
||||
firstDatedCollectInvite: FIRST_DATED_COLLECT_INVITE,
|
||||
uncertaintyStop: "前面几道题你多半选了\"说不好\",再问下去也分不开,先停在这里。",
|
||||
tiedFirstStop: "几个候选打成平手,问题已经分不开它们。",
|
||||
probePoolExhaustedStop: "能分开候选的问题已经问完,先按现有材料给你结果。",
|
||||
@@ -428,6 +438,7 @@ export function listUserVisibleCopy(): string[] {
|
||||
RECTIFICATION_USER_COPY.collectHandoff,
|
||||
RECTIFICATION_USER_COPY.collectDeclinedAck,
|
||||
RECTIFICATION_USER_COPY.collectSkippedAck,
|
||||
FIRST_DATED_COLLECT_INVITE,
|
||||
GENERIC_COLLECT_QUESTION,
|
||||
openingSpokenBody(["04:45", "05:15"]),
|
||||
RECTIFICATION_USER_COPY.uncertaintyStop,
|
||||
|
||||
@@ -84,6 +84,7 @@ import {
|
||||
GENERIC_COLLECT_QUESTION,
|
||||
USER_COLLECT_QUESTION,
|
||||
USER_COLLECT_QUESTION_RETRY,
|
||||
withFirstDatedCollectInvite,
|
||||
} from "../user-copy.ts";
|
||||
|
||||
export { GENERIC_COLLECT_QUESTION };
|
||||
@@ -194,6 +195,7 @@ export type MethodFollowup = Readonly<{
|
||||
selection_score?: number;
|
||||
probe_id?: string;
|
||||
collect_retry?: boolean;
|
||||
invite_more_once?: boolean;
|
||||
block_periods?: Readonly<Record<"A" | "B" | "C", BlockScanBlock>>;
|
||||
widen_windows?: Readonly<Record<"A" | "B", import("./window-widen.ts").WidenWindowOption>>;
|
||||
date_reliability_evidence_id?: string;
|
||||
@@ -1211,7 +1213,12 @@ export function spokenFollowupForUser(
|
||||
const mapped = followup.collect_retry === true
|
||||
? (USER_COLLECT_QUESTION_RETRY[domain] ?? USER_COLLECT_QUESTION[domain])
|
||||
: USER_COLLECT_QUESTION[domain];
|
||||
return mapped ?? null;
|
||||
if (!mapped) return null;
|
||||
const dated = (DATED_COLLECT_ORDER as readonly string[]).includes(domain);
|
||||
if (followup.invite_more_once && dated && followup.collect_retry !== true) {
|
||||
return withFirstDatedCollectInvite(mapped);
|
||||
}
|
||||
return mapped;
|
||||
}
|
||||
|
||||
export const DATED_COLLECT_ORDER = [
|
||||
@@ -1260,15 +1267,29 @@ function datedCollectDomainBlocked(
|
||||
|| answeredYes.has(domain);
|
||||
}
|
||||
|
||||
export function datedCollectAlreadyAsked(
|
||||
declined: ReadonlySet<string>,
|
||||
topics: readonly Readonly<Record<string, unknown>>[] = [],
|
||||
): boolean {
|
||||
if (DATED_COLLECT_ORDER.some((domain) => declined.has(domain))) return true;
|
||||
return DATED_COLLECT_ORDER.some((domain) => domainCollectFocusAsked(topics, domain));
|
||||
}
|
||||
|
||||
export function nextDatedCollectFollowup(
|
||||
evidence: readonly MethodFollowupEvidence[],
|
||||
declined: ReadonlySet<string>,
|
||||
answeredYes: ReadonlySet<string> = new Set(),
|
||||
options?: { askedDatedCollect?: boolean },
|
||||
): MethodFollowup | null {
|
||||
for (const domain of DATED_COLLECT_ORDER) {
|
||||
if (datedCollectDomainBlocked(domain, evidence, declined, answeredYes)) continue;
|
||||
const next = datedCollectFollowup(domain, evidence);
|
||||
if (next) return next;
|
||||
if (next) {
|
||||
return {
|
||||
...next,
|
||||
invite_more_once: options?.askedDatedCollect !== true,
|
||||
};
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -1357,7 +1378,9 @@ export function exhaustionSpokenCollectFollowup(input: {
|
||||
}): MethodFollowup | null {
|
||||
const declined = declinedDomains(input.declinedTopics ?? []);
|
||||
const answeredYes = domainsAnsweredYes(input.answeredProbes, input.eventProbes);
|
||||
const dated = nextDatedCollectFollowup(input.evidence, declined, answeredYes);
|
||||
const dated = nextDatedCollectFollowup(input.evidence, declined, answeredYes, {
|
||||
askedDatedCollect: datedCollectAlreadyAsked(declined, input.declinedTopics ?? []),
|
||||
});
|
||||
if (dated) return dated;
|
||||
if (
|
||||
!declined.has("occupation")
|
||||
@@ -1883,9 +1906,15 @@ export function buildMethodFollowupPlan(input: {
|
||||
const collectRetry = base.intent === "collect_method_evidence"
|
||||
&& typeof base.domain === "string"
|
||||
&& domainCollectFocusAsked(askedRows, base.domain);
|
||||
const inviteMoreOnce = base.intent === "collect_method_evidence"
|
||||
&& typeof base.domain === "string"
|
||||
&& (DATED_COLLECT_ORDER as readonly string[]).includes(base.domain)
|
||||
&& !collectRetry
|
||||
&& !datedCollectAlreadyAsked(declinedDomains(input.declinedTopics ?? []), askedRows);
|
||||
return {
|
||||
...base,
|
||||
...(collectRetry ? { collect_retry: true } : {}),
|
||||
...(inviteMoreOnce ? { invite_more_once: true } : { invite_more_once: false }),
|
||||
choice_frame: attach
|
||||
? buildChoiceFrame(base, {
|
||||
observations: input.observations,
|
||||
@@ -2512,7 +2541,12 @@ export function buildMethodFollowupPlan(input: {
|
||||
&& precisionCard?.choice_frame
|
||||
) {
|
||||
next = precisionCard;
|
||||
} else if ((datedCollect = nextDatedCollectFollowup(input.evidence, declined, answeredYes))) {
|
||||
} else if ((datedCollect = nextDatedCollectFollowup(input.evidence, declined, answeredYes, {
|
||||
askedDatedCollect: datedCollectAlreadyAsked(declined, [
|
||||
...(input.closedCollectFocuses ?? []),
|
||||
...(input.declinedTopics ?? []),
|
||||
]),
|
||||
}))) {
|
||||
next = makeFollowup(datedCollect);
|
||||
} else if (!occupationCovered) {
|
||||
if (meetsAcceptanceEventQuality(input.evidence)) {
|
||||
@@ -2688,7 +2722,12 @@ export function buildMethodFollowupPlan(input: {
|
||||
next = null;
|
||||
}
|
||||
if (!next) {
|
||||
const leftoverDated = nextDatedCollectFollowup(input.evidence, declined, answeredYes);
|
||||
const leftoverDated = nextDatedCollectFollowup(input.evidence, declined, answeredYes, {
|
||||
askedDatedCollect: datedCollectAlreadyAsked(declined, [
|
||||
...(input.closedCollectFocuses ?? []),
|
||||
...(input.declinedTopics ?? []),
|
||||
]),
|
||||
});
|
||||
if (leftoverDated) {
|
||||
next = makeFollowup(leftoverDated);
|
||||
} else if (!occupationCovered) {
|
||||
|
||||
Reference in New Issue
Block a user