Conversation
A new flat encyclopedia about the user, peer to memory and journal. Articles are titled, single-level, and unique per (user_id, title). The user authors via the new Wiki drawer tab; an autonomous background agent maintains articles by reading conversations a day after they settle. The main LLM reaches articles only through the always-on wiki_search tool - articles are never auto-injected. Schema: wiki_articles + three threads columns (last_wiki_processed_msg_id, wiki_claim_holder, wiki_claim_expires_at) + five RPCs (claim_next_thread_for_wiki, mark_thread_wiki_processed_if_claimed, claim_next_pending_wiki_article, save_wiki_article_embedding_if_claimed, search_wiki_articles_by_embedding). The eligibility predicate is the deliberate divergence from journal: newest message must land on a calendar day strictly before today in the user's tz, and reads the timestamp from messages.created_at via a second lateral rather than threads.updated_at. Autonomous agent clones the journal manager/worker/loop scaffolding but uses reflection's tool-call shape internally - runHeadlessToolLoop against a new wikiToolbox (wiki_search, wiki_create, wiki_update, wiki_delete). Pinned to deepseek-v4-flash via AGENT_MODELS.wiki. Pointer-advance is unconditional on done so a no-op cycle still consumes the conversation. Per-article "ask agent to update" runs synchronously on the main thread via WikiAgent.updateOne - one Venice call, response_format=json_object, no tool loop. UI mirrors Journal.svelte's regenerate flow with Accept / Try again / Cancel preview. The prompt explicitly forbids discarding existing facts unless the user asks for them to be removed, and forbids fabrication. Settings adds a wikiAutomaticEnabled toggle (mirroring journal). The wiki shares the journal's tz preference. WIKI_BLOCK in chat-prompt.ts tells the main LLM the wiki exists and to use wiki_search whenever the user references something topical or factual about themselves. Docs: docs/user/wiki.md and docs/dev/wiki.md, both linked from their respective README index pages. Tests updated for the new always-on tool and the new routed key. https://claude.ai/code/session_015XcR7xzLdij66ZbYERUdLH
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
=head1 SYNOPSIS
A new flat encyclopedia about the user, peer to memory and journal.
Articles are titled, single-level (no nesting), and never auto-injected
into the chat - the assistant reaches them only through the always-on
wiki_searchtool.=head1 PURPOSE
Currently the app maintains three forms of long-running user knowledge:
in the system prompt on the first turn.
There is no surface for topical, encyclopedic knowledge that spans
many conversations - "what is the recipe project", "who is Maya", "where
does the Lisbon trip stand". This is the gap the wiki fills.
=head1 DESCRIPTION
=head2 Layer 1: how the existing subsystems behave
The journal worker acquires a cross-tab Web Lock
(
nak:journal-worker), claims a thread viaclaim_next_thread_for_journal(gated on a same-day cooldown usingthreads.updated_at), runsJournalAgent.run(single Venice call,response_format=json_object, no tools), and lands the entry plus thepointer-advance through the atomic
upsert_journal_entry_and_mark_threadRPC. The reflection worker uses the same scaffold but runs
runHeadlessToolLoopagainstmemoryToolbox; tool calls are the sideeffects, the model's final text is discarded. The generic embedding
worker round-robins polymorphic source adapters (memories, threads,
samskara substrate, journal entries), each adapter shaping a
(claim, save)pair against per-table RPCs. The chat-side toolboxhas every read tool registered always-on so the model never has to flip
a toolbox to search; writes still gate.
=head2 Layer 2: what this PR adds, parallel to layer 1
The wiki worker acquires
nak:wiki-worker, claims viaclaim_next_thread_for_wiki, runsWikiAgent.run(which usesrunHeadlessToolLoopagainst the newwikiToolbox), and lands thepointer-advance through
mark_thread_wiki_processed_if_claimed-unconditionally on
done. Two deliberate divergences from thejournal claim RPC:
threads.updated_at. Theeligibility lateral reads
messages.created_atdirectly via asecond lateral so the gate stays stable against future bumps to
threads.updated_atfrom unrelated writes.(newest.created_at at time zone tz)::date < (now() at time zone tz)::date.Effect: chat Monday -> eligible Tuesday; resume Wednesday -> the
newest msg lands on Wednesday and the inequality fails again until
Thursday.
The wiki embedding adapter is a clone of the memories adapter (input
shape
${title}\n\n${content}, 16k char cap, same claim/save pair). Thegeneric worker picks it up alongside the existing four sources.
wiki_searchis registered in the always-on toolbox so every chatturn can call it without a toolbox toggle.
wiki_create/wiki_update/wiki_deletelive only in the agent-onlywikiToolbox-the main LLM has no path to write to the wiki.
The per-article manual flow (
WikiAgent.updateOne) runs synchronouslyon the main thread: one Venice call,
response_format=json_object, notool loop. UI mirrors
Journal.svelte's regenerate flow withAccept / Try again / Cancel preview. The prompt explicitly forbids
discarding existing facts unless the user asks for them removed, and
forbids fabrication.
unique(user_id, title)letswiki_createraise on collision; the toolrephrases the unique-violation as actionable text the autonomous agent
reads and falls through to
wiki_search+wiki_update.A new WIKI_BLOCK in
src/lib/chat-prompt.tstells the main LLM thewiki exists and to call
wiki_searchwhenever the user referencessomething topical or factual about themselves, a project, a person, or
a place. Articles are explicitly never auto-injected.
A new
wikiAutomaticEnabledsetting (defaulting on, mirroringjournalAutomaticEnabled) gates the autonomous worker. Manual edits andthe per-article "ask agent to update" button keep working when off. The
wiki shares the journal's tz preference - one user-tz setting covers
both background subsystems.
=head2 Layer 3: how this resolves PURPOSE
The user (and a background agent) now have a fourth long-running surface
sized for "what is X" rather than atomic facts or dated reflections. The
assistant can ground "what's the recipe project about" answers without
guessing. Articles accrete coherently because the autonomous agent
search-then-mutates and is told to preserve facts. The user can still
correct, rewrite, or delete by hand, or hand the agent explicit
instructions for a single article.
Notes for AI reviewers:
wiki_delete(unlikememoryToolboxwhich usesmemory_invalidate). The promptconstrains it to consolidation only - "an article you just updated
now strictly subsumes another one". Never delete on the basis of
"the user said something different today" alone.
done. Even a no-op cycleadvances the pointer; otherwise every cycle would re-process the
same "no topic warranted writing" conversation.
embedding_claim_expires(no_at) onwiki_articlesis intentional -matches the existing memories / journal_entries convention. The
thread-side
wiki_claim_expires_atDOES have the suffix, matchingthe journal claim columns.
conversation explicitly contradicts it" line is load-bearing. A
reviewer suggesting "let the agent rewrite for clarity" would
silently churn the wiki.
https://claude.ai/code/session_015XcR7xzLdij66ZbYERUdLH
Generated by Claude Code