@resolveio/server-lib 22.3.190 → 22.3.191

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.
@@ -239,6 +239,22 @@ export interface ResolveIOSupportCustomerReplyPolicyInput {
239
239
  releasePolicy?: ResolveIOAIManagerHotfixFirstReleasePolicy;
240
240
  releaseGatePassed?: boolean;
241
241
  }
242
+ export type ResolveIOSupportClarificationMissingField = 'reproduction_blocker' | 'expected_result' | 'observed_result' | 'account_customer_context' | 'example_record_or_action_path';
243
+ export interface ResolveIOSupportClarificationContract {
244
+ contractId: string;
245
+ status: 'ready' | 'blocked';
246
+ missingField: ResolveIOSupportClarificationMissingField;
247
+ question: string;
248
+ sourceBlockers: string[];
249
+ oneQuestionOnly: boolean;
250
+ customerFacingDraftAllowed: boolean;
251
+ customerSendAllowed: boolean;
252
+ parksTicketUntilCustomerReply: boolean;
253
+ nextCommands: string[];
254
+ forbiddenActions: string[];
255
+ requiredEvidence: string[];
256
+ createdAt: string;
257
+ }
242
258
  export interface ResolveIOSupportCustomerReplyPolicy {
243
259
  action: ResolveIOSupportCustomerReplyAction;
244
260
  canDraftCustomerReply: boolean;
@@ -248,6 +264,7 @@ export interface ResolveIOSupportCustomerReplyPolicy {
248
264
  reason: string;
249
265
  requiredEvidence: string[];
250
266
  clarificationQuestion?: string;
267
+ clarificationContract?: ResolveIOSupportClarificationContract;
251
268
  humanReviewPacket?: ResolveIOSupportHumanReviewPacket;
252
269
  draftBasis?: {
253
270
  issueClass: ResolveIOSupportIssueClass;
@@ -835,6 +852,13 @@ export declare function validateResolveIOSupportDiagnosisGate(value: any, option
835
852
  maxOwnerFiles?: number;
836
853
  }): ResolveIOSupportDiagnosisGateValidation;
837
854
  export declare function evaluateResolveIOSupportBusinessProofReadiness(input?: ResolveIOSupportBusinessProofReadinessInput): ResolveIOSupportBusinessProofReadiness;
855
+ export declare function buildResolveIOSupportClarificationContract(gate: ResolveIOSupportDiagnosisGate | undefined, blockers?: string[], now?: Date | string): ResolveIOSupportClarificationContract;
856
+ export declare function validateResolveIOSupportClarificationContract(value: any): {
857
+ valid: boolean;
858
+ status: 'ready' | 'blocked';
859
+ blockers: string[];
860
+ normalized: ResolveIOSupportClarificationContract;
861
+ };
838
862
  export declare function decideResolveIOSupportCustomerReplyPolicy(input?: ResolveIOSupportCustomerReplyPolicyInput): ResolveIOSupportCustomerReplyPolicy;
839
863
  export declare function buildResolveIOSupportIssueClassProbes(value: any): ResolveIOSupportIssueClassProbe[];
840
864
  export declare function validateResolveIOSupportIssueClassProbePlan(value: any, diagnosisGate?: any, now?: Date | string): ResolveIOSupportIssueClassProbePlanValidation;
@@ -55,6 +55,8 @@ exports.normalizeResolveIOSupportDiagnosisGate = normalizeResolveIOSupportDiagno
55
55
  exports.extractResolveIOSupportDiagnosisGateFromText = extractResolveIOSupportDiagnosisGateFromText;
56
56
  exports.validateResolveIOSupportDiagnosisGate = validateResolveIOSupportDiagnosisGate;
57
57
  exports.evaluateResolveIOSupportBusinessProofReadiness = evaluateResolveIOSupportBusinessProofReadiness;
58
+ exports.buildResolveIOSupportClarificationContract = buildResolveIOSupportClarificationContract;
59
+ exports.validateResolveIOSupportClarificationContract = validateResolveIOSupportClarificationContract;
58
60
  exports.decideResolveIOSupportCustomerReplyPolicy = decideResolveIOSupportCustomerReplyPolicy;
59
61
  exports.buildResolveIOSupportIssueClassProbes = buildResolveIOSupportIssueClassProbes;
60
62
  exports.validateResolveIOSupportIssueClassProbePlan = validateResolveIOSupportIssueClassProbePlan;
@@ -1628,6 +1630,105 @@ function buildSupportClarificationQuestion(gate, blockers) {
1628
1630
  }
1629
1631
  return 'Can you send one concrete example record, screen, or action path where this issue still occurs?';
1630
1632
  }
1633
+ function resolveSupportClarificationMissingField(gate, blockers) {
1634
+ if ((gate === null || gate === void 0 ? void 0 : gate.issue_case.reproduction_status) === 'blocked' && gate.issue_case.reproduction_blocker) {
1635
+ return 'reproduction_blocker';
1636
+ }
1637
+ if (blockers.some(function (blocker) { return /expected_result/.test(blocker); })) {
1638
+ return 'expected_result';
1639
+ }
1640
+ if (blockers.some(function (blocker) { return /observed_result/.test(blocker); })) {
1641
+ return 'observed_result';
1642
+ }
1643
+ if (blockers.some(function (blocker) { return /account_customer_context/.test(blocker); })) {
1644
+ return 'account_customer_context';
1645
+ }
1646
+ return 'example_record_or_action_path';
1647
+ }
1648
+ function buildResolveIOSupportClarificationContract(gate, blockers, now) {
1649
+ if (blockers === void 0) { blockers = []; }
1650
+ var sourceBlockers = cleanList(blockers, 20, 500);
1651
+ var missingField = resolveSupportClarificationMissingField(gate, sourceBlockers);
1652
+ var question = buildSupportClarificationQuestion(gate, sourceBlockers);
1653
+ var createdAt = isoNow(now);
1654
+ return {
1655
+ contractId: "support-clarification-".concat(hashResolveIOSupportV5Evidence({
1656
+ missingField: missingField,
1657
+ question: question,
1658
+ sourceBlockers: sourceBlockers,
1659
+ ticketRoute: gate === null || gate === void 0 ? void 0 : gate.issue_case.route_module,
1660
+ createdAt: createdAt.slice(0, 16)
1661
+ }).slice(0, 16)),
1662
+ status: 'ready',
1663
+ missingField: missingField,
1664
+ question: question,
1665
+ sourceBlockers: sourceBlockers,
1666
+ oneQuestionOnly: (question.match(/\?/g) || []).length === 1,
1667
+ customerFacingDraftAllowed: true,
1668
+ customerSendAllowed: false,
1669
+ parksTicketUntilCustomerReply: true,
1670
+ nextCommands: ['review_customer_clarification', 'send_after_human_review', 'park_ticket_until_customer_reply'],
1671
+ forbiddenActions: [
1672
+ 'Do not run product repair from a guessed reproduction path.',
1673
+ 'Do not ask multiple customer questions in one automated clarification.',
1674
+ 'Do not mark the ticket resolved from a clarification draft.'
1675
+ ],
1676
+ requiredEvidence: [
1677
+ "missing_field=".concat(missingField),
1678
+ 'one customer-facing question',
1679
+ 'human approval before send',
1680
+ 'park_ticket_until_customer_reply'
1681
+ ],
1682
+ createdAt: createdAt
1683
+ };
1684
+ }
1685
+ function validateResolveIOSupportClarificationContract(value) {
1686
+ var source = cleanObject(value);
1687
+ var missingField = cleanText(source.missingField || source.missing_field, 120);
1688
+ var question = cleanText(source.question, 1000);
1689
+ var sourceBlockers = cleanList(source.sourceBlockers || source.source_blockers, 20, 500);
1690
+ var questionMarkCount = (question.match(/\?/g) || []).length;
1691
+ var normalized = {
1692
+ contractId: cleanText(source.contractId || source.contract_id, 160),
1693
+ status: source.status === 'ready' ? 'ready' : 'blocked',
1694
+ missingField: missingField,
1695
+ question: question,
1696
+ sourceBlockers: sourceBlockers,
1697
+ oneQuestionOnly: source.oneQuestionOnly === true || source.one_question_only === true || questionMarkCount === 1,
1698
+ customerFacingDraftAllowed: source.customerFacingDraftAllowed === true || source.customer_facing_draft_allowed === true,
1699
+ customerSendAllowed: source.customerSendAllowed === true || source.customer_send_allowed === true,
1700
+ parksTicketUntilCustomerReply: source.parksTicketUntilCustomerReply === true || source.parks_ticket_until_customer_reply === true,
1701
+ nextCommands: cleanList(source.nextCommands || source.next_commands, 12, 200),
1702
+ forbiddenActions: cleanList(source.forbiddenActions || source.forbidden_actions, 12, 500),
1703
+ requiredEvidence: cleanList(source.requiredEvidence || source.required_evidence, 12, 500),
1704
+ createdAt: cleanText(source.createdAt || source.created_at, 120)
1705
+ };
1706
+ var validMissingFields = [
1707
+ 'reproduction_blocker',
1708
+ 'expected_result',
1709
+ 'observed_result',
1710
+ 'account_customer_context',
1711
+ 'example_record_or_action_path'
1712
+ ];
1713
+ var blockers = [
1714
+ normalized.contractId ? '' : 'Clarification contract is missing contractId.',
1715
+ validMissingFields.includes(normalized.missingField) ? '' : 'Clarification contract is missing a valid missingField.',
1716
+ question.length >= 20 && question.length <= 500 ? '' : 'Clarification question must be 20-500 characters.',
1717
+ questionMarkCount === 1 && normalized.oneQuestionOnly ? '' : 'Clarification contract must contain exactly one customer question.',
1718
+ normalized.customerFacingDraftAllowed === true ? '' : 'Clarification draft must be customer-facing so the operator can review it.',
1719
+ normalized.customerSendAllowed === false ? '' : 'Clarification contract must never allow automatic customer send.',
1720
+ normalized.parksTicketUntilCustomerReply === true ? '' : 'Clarification contract must park the ticket until customer reply.',
1721
+ normalized.nextCommands.includes('park_ticket_until_customer_reply') ? '' : 'Clarification nextCommands must include park_ticket_until_customer_reply.',
1722
+ normalized.forbiddenActions.length ? '' : 'Clarification contract must include forbiddenActions.'
1723
+ ].filter(Boolean);
1724
+ normalized.status = blockers.length ? 'blocked' : 'ready';
1725
+ return {
1726
+ valid: blockers.length === 0,
1727
+ status: normalized.status,
1728
+ blockers: blockers,
1729
+ normalized: normalized
1730
+ };
1731
+ }
1631
1732
  function buildResolveIOSupportHumanReviewPacket(input) {
1632
1733
  return {
1633
1734
  reviewType: input.reviewType,
@@ -1708,7 +1809,33 @@ function decideResolveIOSupportCustomerReplyPolicy(input) {
1708
1809
  var canAskCustomer = (gate === null || gate === void 0 ? void 0 : gate.issue_case.reproduction_status) === 'blocked'
1709
1810
  || diagnosisValidation.blockers.some(function (blocker) { return /expected_result|observed_result|account_customer_context/.test(blocker); });
1710
1811
  if (canAskCustomer) {
1711
- var clarificationQuestion = buildSupportClarificationQuestion(gate, diagnosisValidation.blockers);
1812
+ var clarificationValidation = validateResolveIOSupportClarificationContract(buildResolveIOSupportClarificationContract(gate, diagnosisValidation.blockers));
1813
+ var clarificationContract = clarificationValidation.normalized;
1814
+ if (!clarificationValidation.valid) {
1815
+ return {
1816
+ action: 'hold_internal',
1817
+ canDraftCustomerReply: false,
1818
+ canSendCustomerReply: false,
1819
+ confidenceLevel: confidenceLevel,
1820
+ safety: 'internal_hold',
1821
+ reason: 'support_reply_clarification_contract_invalid',
1822
+ requiredEvidence: requiredEvidence,
1823
+ clarificationContract: clarificationContract,
1824
+ humanReviewPacket: buildResolveIOSupportHumanReviewPacket({
1825
+ reviewType: 'internal_hold',
1826
+ title: 'Repair Clarification Contract',
1827
+ summary: 'Customer clarification is blocked until the one-question clarification contract validates.',
1828
+ primaryAction: 'repair_support_clarification_contract',
1829
+ safety: 'internal_hold',
1830
+ reason: 'support_reply_clarification_contract_invalid',
1831
+ blockers: clarificationValidation.blockers,
1832
+ requiredEvidence: requiredEvidence,
1833
+ nextCommands: ['repair_clarification_contract', 'rerun_reply_policy'],
1834
+ forbiddenActions: ['Do not send a malformed or multi-question clarification.'],
1835
+ costRisk: 'free_or_deterministic'
1836
+ })
1837
+ };
1838
+ }
1712
1839
  return {
1713
1840
  action: 'ask_clarification',
1714
1841
  canDraftCustomerReply: true,
@@ -1716,21 +1843,22 @@ function decideResolveIOSupportCustomerReplyPolicy(input) {
1716
1843
  confidenceLevel: confidenceLevel,
1717
1844
  safety: 'needs_clarification',
1718
1845
  reason: 'support_reply_waiting_on_customer_reproduction_detail',
1719
- requiredEvidence: requiredEvidence,
1720
- clarificationQuestion: clarificationQuestion,
1846
+ requiredEvidence: Array.from(new Set(__spreadArray(__spreadArray([], __read(requiredEvidence), false), __read(clarificationContract.requiredEvidence), false))),
1847
+ clarificationQuestion: clarificationContract.question,
1848
+ clarificationContract: clarificationContract,
1721
1849
  humanReviewPacket: buildResolveIOSupportHumanReviewPacket({
1722
1850
  reviewType: 'customer_clarification',
1723
1851
  title: 'Review Customer Clarification',
1724
1852
  summary: 'The runner needs one customer detail before it can reproduce or continue safely.',
1725
1853
  primaryAction: 'review_customer_clarification',
1726
- question: clarificationQuestion,
1727
- customerFacingDraftAllowed: true,
1854
+ question: clarificationContract.question,
1855
+ customerFacingDraftAllowed: clarificationContract.customerFacingDraftAllowed,
1728
1856
  safety: 'needs_clarification',
1729
1857
  reason: 'support_reply_waiting_on_customer_reproduction_detail',
1730
- blockers: diagnosisValidation.blockers,
1731
- requiredEvidence: requiredEvidence,
1732
- nextCommands: ['edit_clarification_question', 'send_after_human_review', 'park_ticket_until_customer_reply'],
1733
- forbiddenActions: ['Do not run repair from a guessed reproduction path.'],
1858
+ blockers: clarificationContract.sourceBlockers,
1859
+ requiredEvidence: clarificationContract.requiredEvidence,
1860
+ nextCommands: clarificationContract.nextCommands,
1861
+ forbiddenActions: clarificationContract.forbiddenActions,
1734
1862
  costRisk: 'release_or_customer_send'
1735
1863
  })
1736
1864
  };