export type CommandOrigin = 'user' | 'ai' | 'automation' | 'unknown'; export type CommandAuditInput = { /** 클라이언트가 실어 보낸 커맨드(신뢰하지 않는 부분을 포함한 원본). */ command: any; /** 커널 판정. */ ack: { accepted?: boolean; errorCode?: string; error?: string; }; /** * **원본(어댑터)이 준 답** — 허락과 일어남 사이에 끼어 있는 것을 같은 기록에 닫는다 (ADR-0029 §1 결정 C). * * ── 왜 이 칸이 필요한가 ────────────────────────────────────────────────── * 명령이 원본으로 가면 감사는 「허락했다」까지만 증명한다 — 어댑터가 오류 없이 실패하면 원장에는 승인만 * 남는다. 그래서 어댑터의 답을 **같은 행**에 되적는다. * * 셋을 구별한다: * · 없음(`undefined`) — **원본에 묻지 않았다**(커널이 받은 명령이다). 「모른다」와 다르다. * · `{ answered: false }` — 물었는데 **답을 못 받았다** → `unknown` 으로 남긴다. 성공으로 적지 않는다. * · `{ answered: true, ok, code? }` — 원본이 답했다(적용·거절 모두 사실이다). */ source?: { answered: boolean; ok?: boolean; code?: string; }; /** 세션이 아는 사용자 — **서버만 채운다.** */ actor?: { id?: string; email?: string; name?: string; }; /** 서버 시각. 호출부가 주입해 테스트가 시간을 고정할 수 있게 한다. */ at: Date; }; /** `TwinAuditEvent` 에 그대로 넣을 수 있는 모양(엔티티를 몰라도 되게 순수하게 둔다). */ export type CommandAuditRow = { eventTime: Date; commandId?: string; commandType?: string; origin: CommandOrigin; reason?: string; actorId?: string; actorEmail?: string; actorName?: string; accepted: boolean; errorCode?: string; /** * 원본이 그래서 어떻게 했나 — `applied` · `refused` · `unknown`(물었지만 답이 없다). * **없으면 묻지 않은 것이다**(커널이 받은 명령). 0 이나 성공으로 채우지 않는다. */ sourceOutcome?: 'applied' | 'refused' | 'unknown'; /** 원본이 준 사유 코드 — 있으면 그대로. 우리가 지어내지 않는다. */ sourceCode?: string; args?: any; argsTruncated: boolean; }; /** 신뢰할 수 있는 출처 표기로 정규화 — 모르는 값은 지어내지 않고 'unknown' 으로 둔다. */ export declare function normalizeOrigin(v: unknown): CommandOrigin; export declare function buildCommandAudit(input: CommandAuditInput): CommandAuditRow; /** * 원본의 답을 세 값으로 — **답이 없으면 `unknown`**(성공으로 적지 않는다). * * 이 함수가 있는 이유: 「물었지만 답이 없다」를 실패로도 성공으로도 적을 수 없다. 실패로 적으면 일어난 일을 * 없던 일로 만들고, 성공으로 적으면 없던 일을 일어난 일로 만든다. 셋째 값이 사실이다. */ export declare function sourceOutcomeOf(source: { answered: boolean; ok?: boolean; }): 'applied' | 'refused' | 'unknown';