feat(curator): pending-action API routes + client helpers (C4/5)
The HTTP surface for the review queue. Three endpoints, all under
the existing /api/journal blueprint to keep the journal-related routes
together:
- GET /api/journal/pending — list current user's pending actions.
- POST /api/journal/pending/<id>/approve — replay the proposed tool
call via execute_tool(authority='user'). On success, marks
the row 'approved'; on replay error, leaves it pending so
the user can retry.
- POST /api/journal/pending/<id>/reject — marks 'rejected' with no
execution.
Each route is a thin wrapper around services/pending_actions and
delegates user-scoping to the service (which checks user_id on every
load — actions are private to the proposer).
api/client.ts:
- PendingCuratorAction interface mirroring the backend dict shape:
id, user_id, conv_id, action_type, target_type/id/label, payload,
current_snapshot, status, timestamps.
- listPendingActions / approvePendingAction / rejectPendingAction
helpers for the upcoming Needs Review panel.
C5 next: the panel itself.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -423,6 +423,36 @@ export async function runJournalCurator(convId: number): Promise<CuratorRunResul
|
||||
return apiPost<CuratorRunResult>(`/api/journal/curator/run/${convId}`, {});
|
||||
}
|
||||
|
||||
// Curator-proposed mutations awaiting user review (Needs Review panel).
|
||||
// See routes/journal.py pending_actions endpoints.
|
||||
|
||||
export interface PendingCuratorAction {
|
||||
id: number;
|
||||
user_id: number;
|
||||
conv_id: number | null;
|
||||
action_type: string; // e.g. "update_note", "delete_note", "update_milestone"
|
||||
target_type: string | null; // "task" | "note" | "milestone" | "project" | "profile"
|
||||
target_id: number | null;
|
||||
target_label: string | null; // human-readable title for the card header
|
||||
payload: Record<string, unknown>; // the curator's proposed args
|
||||
current_snapshot: Record<string, unknown>; // target state at proposal time
|
||||
status: 'pending' | 'approved' | 'rejected';
|
||||
created_at: string;
|
||||
reviewed_at: string | null;
|
||||
}
|
||||
|
||||
export async function listPendingActions(): Promise<{ pending: PendingCuratorAction[]; count: number }> {
|
||||
return apiGet<{ pending: PendingCuratorAction[]; count: number }>('/api/journal/pending');
|
||||
}
|
||||
|
||||
export async function approvePendingAction(actionId: number): Promise<Record<string, unknown>> {
|
||||
return apiPost<Record<string, unknown>>(`/api/journal/pending/${actionId}/approve`, {});
|
||||
}
|
||||
|
||||
export async function rejectPendingAction(actionId: number): Promise<Record<string, unknown>> {
|
||||
return apiPost<Record<string, unknown>>(`/api/journal/pending/${actionId}/reject`, {});
|
||||
}
|
||||
|
||||
export async function listJournalMoments(params: Record<string, string | number | boolean> = {}): Promise<JournalMoment[]> {
|
||||
const qs = new URLSearchParams();
|
||||
for (const [k, v] of Object.entries(params)) qs.set(k, String(v));
|
||||
|
||||
Reference in New Issue
Block a user