Add shared notes with tabbed pages editable by user and AI

Adds a note_pages table backing a tabbed notes panel where the user and
the model can both read and write. Only page titles (plus a short
preview) are injected into the system prompt on every turn; full page
content is kept out of context and fetched on demand via a read_note
tool, with write_note letting the model append to, replace, or create a
page.

Generalizes llm.resolve_web_search into resolve_tool_calls so the
tool-calling loop handles web_search and the new note tools through one
path. The chat stream emits a notes_updated SSE event when the model
edits a note so the frontend panel can refresh; local edits are tracked
as dirty and never clobbered by that refresh.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-19 01:27:31 +09:00
co-authored by Claude Opus 5
parent ef588d99d8
commit a08b1785a4
16 changed files with 760 additions and 26 deletions
+39 -1
View File
@@ -22,7 +22,7 @@ export interface Source {
}
export interface SSEChunk {
type: 'session_id' | 'chunk' | 'done' | 'error' | 'sources'
type: 'session_id' | 'chunk' | 'done' | 'error' | 'sources' | 'notes_updated'
value?: string | Source[]
}
@@ -257,3 +257,41 @@ export function streamChat(
return () => controller.abort()
}
export interface NotePage {
id: number
title: string
content: string
position: number
updated_at: string
}
export async function fetchNotePages(): Promise<NotePage[]> {
const res = await fetch(`${BASE}/api/notes`)
return parseJson<NotePage[]>(res, 'ノート取得')
}
export async function createNotePage(title: string): Promise<NotePage> {
const res = await fetch(`${BASE}/api/notes`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ title }),
})
return parseJson<NotePage>(res, 'ページ作成')
}
export async function updateNotePage(
pageId: number,
patch: { title?: string; content?: string },
): Promise<NotePage> {
const res = await fetch(`${BASE}/api/notes/${pageId}`, {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(patch),
})
return parseJson<NotePage>(res, 'ページ更新')
}
export async function deleteNotePage(pageId: number): Promise<void> {
await fetch(`${BASE}/api/notes/${pageId}`, { method: 'DELETE' })
}