tftsr-devops_investigation/src/stores/sessionStore.ts
Shaun Arman e9c576f606
Some checks failed
Test / rust-fmt-check (pull_request) Successful in 2m3s
Test / frontend-tests (pull_request) Successful in 1m56s
Test / frontend-typecheck (pull_request) Successful in 1m58s
Test / rust-clippy (pull_request) Failing after 3m0s
Test / rust-tests (pull_request) Successful in 4m22s
PR Review Automation / review (pull_request) Successful in 4m35s
fix(security): frontend attachment scan notice, bubble redaction update, fmt fix
Addresses three findings from the third automated review:

[BLOCKER] No frontend PII pre-check on attachments.
Added detectPiiCmd call for each logFileId before chatMessageCmd.
PII is not blocked (per explicit product decision: auto-redact and
send) but the user now sees a non-blocking amber notice listing
each file and the PII types that will be auto-redacted. Backend
remains the authoritative redaction layer.

[WARNING 2] Chat bubble showed original PII-laden message even though
only the redacted form was sent to AI.
Added updateMessageContent to sessionStore. After chatMessageCmd
returns, if response.user_message is set the user bubble is updated
to reflect what was actually stored in the DB, so the UI is
consistent with the audit log.

CI fix: cargo fmt changes to analysis.rs were not staged in the prior
commit. Committed here — fmt check now passes cleanly.
2026-05-31 19:49:21 -05:00

57 lines
2.0 KiB
TypeScript

import { create } from "zustand";
import type { Issue, TriageMessage, PiiSpan, ResolutionStep } from "@/lib/tauriCommands";
interface SessionState {
currentIssue: Issue | null;
messages: TriageMessage[];
piiSpans: PiiSpan[];
approvedRedactions: PiiSpan[];
currentWhyLevel: number;
activeDomain: string;
resolutionSteps: ResolutionStep[];
isLoading: boolean;
error: string | null;
startSession: (issue: Issue) => void;
addMessage: (message: TriageMessage) => void;
updateMessageContent: (id: string, content: string) => void;
setPiiSpans: (spans: PiiSpan[]) => void;
setApprovedRedactions: (spans: PiiSpan[]) => void;
setWhyLevel: (level: number) => void;
setActiveDomain: (domain: string) => void;
setResolutionSteps: (steps: ResolutionStep[]) => void;
setLoading: (loading: boolean) => void;
setError: (error: string | null) => void;
reset: () => void;
}
const initialState = {
currentIssue: null,
messages: [],
piiSpans: [],
approvedRedactions: [],
currentWhyLevel: 0,
activeDomain: "general",
resolutionSteps: [],
isLoading: false,
error: null,
};
export const useSessionStore = create<SessionState>((set) => ({
...initialState,
startSession: (issue) => set({ currentIssue: issue, messages: [], currentWhyLevel: 1, activeDomain: issue.category }),
addMessage: (message) => set((state) => ({ messages: [...state.messages, message] })),
updateMessageContent: (id, content) =>
set((state) => ({
messages: state.messages.map((m) => (m.id === id ? { ...m, content } : m)),
})),
setPiiSpans: (spans) => set({ piiSpans: spans }),
setApprovedRedactions: (spans) => set({ approvedRedactions: spans }),
setWhyLevel: (level) => set({ currentWhyLevel: level }),
setActiveDomain: (domain) => set({ activeDomain: domain }),
setResolutionSteps: (steps) => set({ resolutionSteps: steps }),
setLoading: (loading) => set({ isLoading: loading }),
setError: (error) => set({ error }),
reset: () => set(initialState),
}));