Files
Jyotisha/frontend/src/lib/rectification-agentic/v9/public-receipt.ts
T
Jesse_ChenandCursor fe87a9ecdb fix(web): isolate rectification answers from tool-step planning text
Mastra intermediate text-delta was published as answer.delta, then set-focus domain errors reset the attempt and replayed evidence. Publish only the terminal no-tool step, persist the next probe on the server, and ground batch quotes in the source turn.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-24 21:26:57 +08:00

231 lines
6.6 KiB
TypeScript

/**
* V9 public execution receipts and activity event allowlists.
*
* The web client may only ever see allowlisted phases, tool names and
* activity events. Raw provider reasoning payloads, tool payloads, internal
* scores, birth data and provider metadata must never reach the client.
* Chain-of-thought is never a public event. Coarse `activity.changed`
* replaces token-level thinking on the browser stream.
*/
export const PUBLIC_RECTIFICATION_PHASES = [
"run.started",
"skill.started",
"skill.loaded",
"skill.bound",
"case.loaded",
"intent.classified",
"evidence.proposed",
"evidence.confirmed",
"candidates.comparing",
"candidates.updated",
"diagnostics.completed",
"candidate.accepted",
"birth_time.confirmed",
"answer.composed",
"billing.settled",
"answer.delta",
"activity.changed",
"choice.applied",
"attempt.reset",
"run.completed",
"run.failed",
] as const;
export type PublicRectificationPhase =
(typeof PUBLIC_RECTIFICATION_PHASES)[number];
export const PUBLIC_RECTIFICATION_TOOLS = [
"rectification-read-case",
"rectification-set-focus",
"rectification-resolve-focus",
"rectification-record-evidence-batch",
"rectification-propose-evidence",
"rectification-confirm-evidence",
"rectification-revise-evidence",
"rectification-compare-candidates",
"rectification-read-diagnostics",
"rectification-offer-candidates",
"rectification-accept-candidate",
"rectification-confirm-birth-time",
"rectification-close-case",
] as const;
export type PublicRectificationTool =
(typeof PUBLIC_RECTIFICATION_TOOLS)[number];
export const RECTIFICATION_AGENT_TOOLS = PUBLIC_RECTIFICATION_TOOLS.filter(
(tool) => tool !== "rectification-set-focus",
);
export const PUBLIC_RECTIFICATION_METHODS = [
"d1-rashi",
"d2-hora",
"d3-drekkana",
"d4-chaturthamsha",
"d5-panchamsha",
"d7-saptamsha",
"d9-navamsa",
"d10-dashamsa",
"d11-labhamsha",
"d12-dwadashamsha",
"d24-chaturvimshamsha",
"d30-trimshamsha",
"vimshottari-dasha",
"narayana-dasha",
"gochara",
"ashtakavarga",
"shadbala",
"arudha-pada",
"functional-benefic-malefic",
] as const;
export type PublicRectificationMethod =
(typeof PUBLIC_RECTIFICATION_METHODS)[number];
export const RECTIFICATION_ACTIVITY_STATUSES = [
"started",
"completed",
"failed",
] as const;
export type RectificationActivityStatus =
(typeof RECTIFICATION_ACTIVITY_STATUSES)[number];
export const PUBLIC_RECTIFICATION_ACTIVITIES = [
"reading_case",
"recording_answer",
"updating_candidates",
"checking_stability",
"preparing_question",
"preparing_result",
] as const;
export type PublicRectificationActivity =
(typeof PUBLIC_RECTIFICATION_ACTIVITIES)[number];
export type RectificationActivityChangedEvent = Readonly<{
type: "activity.changed";
activity: PublicRectificationActivity;
}>;
export type RectificationChoiceAppliedEvent = Readonly<{
type: "choice.applied";
questionId: string;
}>;
export type RectificationActivityEvent = Readonly<{
type: "tool.activity";
tool: PublicRectificationTool;
status: RectificationActivityStatus;
methods?: readonly PublicRectificationMethod[];
code?: string;
}>;
export const RECEIPT_STATUSES = [
"completed",
"degraded",
"blocked",
"failed",
] as const;
export type RectificationReceiptStatus =
(typeof RECEIPT_STATUSES)[number];
export type RectificationExecutionReceipt = Readonly<{
turnId: string;
skillName: string;
skillVersion: string;
engineVersion: string | null;
phases: readonly PublicRectificationPhase[];
toolsUsed: readonly PublicRectificationTool[];
methods: readonly PublicRectificationMethod[];
status: RectificationReceiptStatus;
startedAt: string;
completedAt: string;
}>;
const PHASE_SET = new Set<string>(PUBLIC_RECTIFICATION_PHASES);
const TOOL_SET = new Set<string>(PUBLIC_RECTIFICATION_TOOLS);
const METHOD_SET = new Set<string>(PUBLIC_RECTIFICATION_METHODS);
const ACTIVITY_STATUS_SET = new Set<string>(RECTIFICATION_ACTIVITY_STATUSES);
const ACTIVITY_SET = new Set<string>(PUBLIC_RECTIFICATION_ACTIVITIES);
export function isPublicRectificationPhase(
value: unknown,
): value is PublicRectificationPhase {
return typeof value === "string" && PHASE_SET.has(value);
}
export function isPublicRectificationTool(
value: unknown,
): value is PublicRectificationTool {
return typeof value === "string" && TOOL_SET.has(value);
}
export function isPublicRectificationMethod(
value: unknown,
): value is PublicRectificationMethod {
return typeof value === "string" && METHOD_SET.has(value);
}
export function isRectificationActivityStatus(
value: unknown,
): value is RectificationActivityStatus {
return typeof value === "string" && ACTIVITY_STATUS_SET.has(value);
}
export function isPublicRectificationActivity(
value: unknown,
): value is PublicRectificationActivity {
return typeof value === "string" && ACTIVITY_SET.has(value);
}
const TOOL_ACTIVITY: Readonly<Partial<Record<PublicRectificationTool, PublicRectificationActivity>>> = {
"rectification-read-case": "reading_case",
"rectification-record-evidence-batch": "recording_answer",
"rectification-propose-evidence": "recording_answer",
"rectification-confirm-evidence": "recording_answer",
"rectification-revise-evidence": "recording_answer",
"rectification-resolve-focus": "recording_answer",
"rectification-compare-candidates": "updating_candidates",
"rectification-read-diagnostics": "checking_stability",
"rectification-set-focus": "preparing_question",
"rectification-offer-candidates": "preparing_result",
"rectification-accept-candidate": "preparing_result",
"rectification-confirm-birth-time": "preparing_result",
"rectification-close-case": "preparing_result",
};
export function activityForRectificationTool(
tool: PublicRectificationTool,
): PublicRectificationActivity {
return TOOL_ACTIVITY[tool] ?? "preparing_question";
}
/**
* The exact NDJSON activity stream events the API is allowed to emit.
* Anything not in this list must be dropped before it reaches the browser.
*/
export const PUBLIC_ACTIVITY_EVENTS = PUBLIC_RECTIFICATION_PHASES;
export function safeActivityEvent(
value: unknown,
): PublicRectificationPhase | null {
return isPublicRectificationPhase(value) ? value : null;
}
/**
* Denied content that must never appear in any public projection.
* The working candidate range is user-visible (BUG-074), but the baseline
* birth snapshot, raw scores and internal identifiers are not.
*/
export const DENIED_PUBLIC_CONTENT = [
"raw provider chain-of-thought payload",
"unsanitized reasoning",
"tool payload",
"baseline birth snapshot",
"user_id",
"engine score",
] as const;