feat(journal): right-rail captures panel + manual curator trigger (Phase 1b)
Frontend half of the conversation+curator architecture. Pairs with the
backend in commit a7002a8. With this commit, you can have a journal
conversation (chat model has no tools, doesn't try to capture), then
press a button and see what the curator extracts.
JournalView.vue:
- New "Captures" section in the right rail, above the existing
"Upcoming" events block. Shows moments from the selected day with
timestamp, content, and entity/task/note chips.
- "Process captures" button (Sparkles icon). Disabled for non-today
days because we're not back-running the curator over historical
conversations. Toast on success/failure with timing + tool-call
count from the CuratorRunResult.
- Captures auto-load on day change AND immediately after a curator
run completes — the right rail reflects current state without a
page reload.
- Bound CSS scoped to the rail: cards with a primary-color left
border, monospaced timestamps, chips for people/places/tasks/notes.
api/client.ts:
- CuratorRunResult type matching the backend dataclass.
- runJournalCurator(convId) helper.
- Pass empty body to apiPost() to satisfy the 2-arg signature
(caller-side fix, not a backend change).
What's not in this commit (deferred):
- The captures panel doesn't show captures from days where the curator
hasn't run yet, even if they would later be captured. Visible only
AFTER a curator pass. (Phase 2's scheduler closes this gap by
running automatically.)
- No edit/delete affordances on captures yet — that comes when we
add the moment-editing UI (out of scope for the conversation+curator
architecture commit chain).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -401,6 +401,28 @@ export async function triggerJournalPrep(date?: string): Promise<{ ok: boolean;
|
||||
return apiPost('/api/journal/trigger-prep', date ? { date } : {});
|
||||
}
|
||||
|
||||
export interface CuratorRunResult {
|
||||
conv_id: number;
|
||||
user_id: number;
|
||||
model: string;
|
||||
messages_examined: number;
|
||||
tool_calls: Array<{
|
||||
name: string;
|
||||
arguments: Record<string, unknown>;
|
||||
status: 'success' | 'error' | 'pending';
|
||||
error: string | null;
|
||||
}>;
|
||||
tools_attempted: number;
|
||||
tools_succeeded: number;
|
||||
summary: string;
|
||||
duration_ms: number;
|
||||
error: string | null;
|
||||
}
|
||||
|
||||
export async function runJournalCurator(convId: number): Promise<CuratorRunResult> {
|
||||
return apiPost<CuratorRunResult>(`/api/journal/curator/run/${convId}`, {});
|
||||
}
|
||||
|
||||
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