Add "My Words" collaborative editor (v1, standalone)#499
Open
kcarnold wants to merge 11 commits into
Open
Conversation
A new sidebar page where the AI can edit the document but may never originate words. Every word it places must be lifted from the writer's own corpus (document + scratchpad + their chat messages), joined only by punctuation and a small closed set of glue words. - corpus.ts: pure, unit-tested phrase-level validator (buildCorpus + validateText + GLUE_WORDS). Lifted spans must appear verbatim in the corpus; new content-word adjacencies are illegal unless glue-bridged. - my-words page: AI tool loop (view / str_replace / insert / highlight) via the ai SDK. Inserted text is validated against a freshly assembled corpus before being applied; rejections are fed back to the model so it retries or asks the writer. AI speech shows as an ephemeral caption with no scrollback; the scratchpad takes most of the height. The model gets lightweight activity signals rather than a full-document dump. - EditorAPI gains getDocText + applyEdit, implemented over Lexical in the standalone editor; Word/Google Docs get getDocText plus typed applyEdit TODO stubs so the same page can drive those hosts later. Wired into the existing page nav (pageContext, navbar, app). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XtzcT6Yo9dVoCHsWgNMQ6U
The Word task pane already renders the shared App + navbar with wordEditorAPI, so the "My Words" tab appears there once edits work. Implement str_replace and insert via Office.js body.search + range.insertText. Edits honor the document's Track Changes mode automatically, so they appear as accept/reject revisions when the user has Track Changes on. Limitation: Word's search is ~255 chars and does not cross paragraph breaks, so this covers sentence/phrase-level edits (how the AI works), not multi-paragraph spans. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XtzcT6Yo9dVoCHsWgNMQ6U
The `view` tool now prefixes each paragraph with its 1-based number, and `insert` accepts a `paragraph` number + `position` to place a new paragraph relative to it. This shared paragraph coordinate system is more robust than text anchoring — and on Word it sidesteps the ~255-char, single-paragraph search limit entirely (uses body.paragraphs + insertParagraph instead of search). - EditorAPI gains getParagraphs(); DocEdit's insert variant gains paragraph/position. - Implemented over Lexical (standalone), Word (body.paragraphs), and a split-based getParagraphs for Google Docs. - System prompt prefers paragraph-targeted inserts and short edits. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XtzcT6Yo9dVoCHsWgNMQ6U
- Tool input schemas use zod (added as a direct dependency) instead of hand-written jsonSchema, for more robust tool-call argument handling. - str_replace / insert now return a lightweight description of the result: the new paragraph count plus a clipped 3-paragraph window around the edit, so the model can track paragraph-number shifts between steps. - str_replace is constrained to a short, single-paragraph span (in both the description and the failure message), since long cross-paragraph old_str cannot be matched in Word and is brittle elsewhere. - System prompt reframed: the AI is a non-directive writing tutor that leads with questions and reflection, while its edits only ever rearrange the writer's own words. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XtzcT6Yo9dVoCHsWgNMQ6U
Past user messages and the scratchpad are part of the corpus, but the model could easily treat chat turns as instructions rather than a word bank. Clarify on three fronts: - System prompt: frame the document + scratchpad + chat messages as one word bank to quote from freely, and state explicitly that the writer's messages are a source of words, not just instructions. - REJECTED tool result: remind the model it can lift from the document, the scratchpad, or anything the writer has said. - `view` now also returns the scratchpad (clearly labeled, unnumbered), so the model can see the full corpus on demand instead of relying only on delta activity notes. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XtzcT6Yo9dVoCHsWgNMQ6U
The activity note previously pushed the full scratchpad into the
transcript on every change — redundant with the scratchpad now shown by
`view`, and it accumulated a fresh full copy per edit over a session.
Make the note a lightweight flag ("scratchpad changed — call view") and
let `view` be the single source of truth for current scratchpad content.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XtzcT6Yo9dVoCHsWgNMQ6U
A `view` result (or any tool result) is a message that persists in the transcript and is re-sent every subsequent turn — so pulling state via `view` is not lighter than pushing it; both land in context, and frequent views accumulate many stale full-document/scratchpad snapshots. Since the document is the source of truth in the editor, the transcript does not need to remember past states. Persist only the conversation (the writer's turns + the assistant's captions) and drop each turn's intermediate tool calls/results. The model re-reads current state with `view` within a turn when it needs it, and that dump is discarded afterward instead of piling up. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XtzcT6Yo9dVoCHsWgNMQ6U
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.
A new sidebar page where the AI can edit the document but may never
originate words. Every word it places must be lifted from the writer's
own corpus (document + scratchpad + their chat messages), joined only by
punctuation and a small closed set of glue words.
validateText + GLUE_WORDS). Lifted spans must appear verbatim in the
corpus; new content-word adjacencies are illegal unless glue-bridged.
via the ai SDK. Inserted text is validated against a freshly assembled
corpus before being applied; rejections are fed back to the model so it
retries or asks the writer. AI speech shows as an ephemeral caption with
no scrollback; the scratchpad takes most of the height. The model gets
lightweight activity signals rather than a full-document dump.
standalone editor; Word/Google Docs get getDocText plus typed applyEdit
TODO stubs so the same page can drive those hosts later.
Wired into the existing page nav (pageContext, navbar, app).
Co-Authored-By: Claude Opus 4.8 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_01XtzcT6Yo9dVoCHsWgNMQ6U