Add SPS Content Studio loop#694
Open
saxster wants to merge 399 commits into
Open
Conversation
The assistant panel now holds multiple conversations as tabs, so several agent runs proceed at once — one planning, one working, one researching. Builds directly on the clientRunId correlation: each streaming run already isolates on the wire, and this isolates them in the UI. - store: assistant slice goes from a single messages[]/thinking to conversations[] + activeConvId. Async flows (runAgent/runWork) CAPTURE their conversation id at start and write only to it, so switching tabs mid-run never lands tokens in the wrong tab. Proposal/db decisions search across all conversations (message ids are globally unique). Conversations auto-title from the first prompt. Always keeps ≥1 tab. - AgentBody: renders the active conversation + a tab strip (switch, close, +new, per-tab running indicator). - verify-agentic-ui.mjs: +2 assertions (tab strip present, +adds a tab) → 11/11. Gates: typecheck (both), eslint, full vitest (1303), verify:note-index, build, live UI smoke 11/11 — all green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…meout headroom Root cause: Skills.tsx renders only a spinner while `loading` is true and mounts the header buttons (New skill / Generate from repo) and tabs only after the initial list IPC calls *resolve* (loadAll → setLoading(false)). Four tests waited for the IPC call to have been *made* (waitFor(expect(api.X).toHaveBeenCalled())) and then did a one-shot getByText/querySelector on those post-load elements, racing the loading→loaded flip. Failed ~1/5 even idle, more under load. Fix: wait for the rendered DOM instead of the IPC call — - header buttons: `await view.findByText(...)` (retries until present), called OUTSIDE act() (nesting findBy's internal act inside an outer act stalls the effect flush — that mistake made loading appear stuck); - Browse tab: a `findBrowseTab` helper that waitFor's the rendered `.skills-tab`. Also bumped vitest testTimeout/hookTimeout to 15s so timing-sensitive RTL tests don't fail purely from CPU starvation when the suite runs alongside other worktrees/builds on the same machine. No production code changed. Verified: typecheck ✓, lint 0/0, 10/10 consecutive full parallel runs green (1343 passed, 3 skipped). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
# Conflicts: # src/main/index.ts # src/main/skills.ts # src/main/sps-agent.ts # src/preload/index.d.ts # src/preload/index.ts # src/renderer/src/screens/Skills/Skills.tsx # src/renderer/src/screens/SpsAgent/assistant/AgentBody.tsx # src/renderer/src/screens/SpsAgent/components/iconPaths.ts # src/renderer/src/screens/SpsAgent/types.ts
Bring personalization out of the admin overlay and into the SPS workspace, and add a structured "How I like things" rules list on top of existing storage. Why: the highest-value personalization capabilities (focus.md daily-context hook, USER.md persona/guardrails, MEMORY.md durable facts) already shipped, but lived only in the admin overlay (gear/⌘,) where normal users never find them, and USER.md guardrails were one free-form blob with no per-item control. - New SPS "You" surface (rail nav + Surface union) mounts focus/persona/durable facts/daily-context-hook where users actually work. - Structured rules: each rule is a plain-English line with an on/off toggle + delete + one-click starter templates. Persisted as a managed `## Rules` section inside USER.md (disabled rules wrapped in HTML comments so the agent never sees them) — NO new IPC/backend; reuses writeUserProfile. - YouSurface is the single owner of USER.md while open, so persona prose and the rules list share one in-memory state + serializer and can't clobber each other. Over-budget saves are refused so the 2200-char window is never truncated. - Extract EditorSection/hookStatusText into Personalization/parts.tsx (shared by the admin screen and the new surface; no behavior change for the overlay). - Pure userMd parse/serialize helper with unit tests; probe-you-surface.mjs proves the surface renders and a rule round-trips to USER.md on disk. Verified: typecheck (both), eslint (touched), vitest (1379 pass incl preload parity + new userMd tests), npm run build, sps-smoke (13 shots), probe. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… Recents menu, remove dead affordances Brings every interactive control on the SPS workspace surface to "functional or removed". Reported issues plus a full dead-control sweep. - Assistant/Comments top-bar buttons are now tab-aware toggles (close only when their tab is active), fixing the intermittent "click does nothing" caused by a blind panelOpen toggle. - Recents chats get right-click + hover-⋯ Rename/Delete, wired to the existing hermesAPI updateSessionTitle/deleteSession; deleting the active chat redirects. - Composer: remove dead file/mention placeholder icons; grounding toggle now flashes a confirmation toast; taller input box (min-height 60px, grows to 200). - Remove the no-op top-bar Share button and the hardcoded TK/PS/SD presence avatars (no sharing/presence backend); delete Presence.tsx + its dead CSS. - Remove orphaned TaskDrawer Share/⋯ buttons. - "Copy link" (page) and "Copy link to block" now copy a resolvable [[wikilink]] to the clipboard with a toast, instead of being no-ops. - Wire onOpenDiagnose: SPS chat surfaces dispatch a hermes:open-settings event; Layout listens and opens Settings. Mic dictation intentionally left unchanged. Verified: typecheck (both projects), lint (clean on touched files), vitest (1370 passed), build, and the sps-smoke harness (13/13). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…it used
Two coupled changes: (1) make the in-workspace SPS co-author actually apply the
enabled USER.md `## Rules` the user builds in the "You" surface, and (2) surface
a dismissable chip on assistant replies showing what the reply was grounded in.
Why: the co-author (assembleVaultContext → spsAssistant) injected vault notes +
MEMORY.md but NOT the USER.md rules, so rules built in "You" had no effect on the
in-workspace assistant. A trust chip claiming "used your rules" would have lied;
fixing the injection makes it honest AND closes the real gap.
- src/shared/userMd.ts: relocated the pure rules parser from the renderer so the
main process can reuse it (parseUserMd now drives both the UI and grounding).
- sps-context.ts: VaultContextInput gains `rules`; formatVaultContext emits a
leading "standing rules — follow these" directive section; assembleVaultContext
reads enabled rules from USER.md and now returns { text, used } where `used` is
a {notes,memory,rules} count (new pure `vaultUsage`, unit-tested).
- sps-agent.ts: AssistantResult carries optional `context`; spsAssistant attaches
the usage summary when anything was injected.
- Renderer: AssistantResult/AgentMessage gain `context`; BridgeAssistant re-attaches
it after validateResult rebuilds the object; runAgent threads it onto bot msgs;
AgentBody renders a dismissable "✦ Used your 2 rules · 1 memory item · 3 notes"
chip (pure contextChipLabel, unit-tested) with sps-scoped CSS.
Verified: typecheck (both), eslint (touched), vitest 1386 pass (incl preload
parity + new userMd/contextChip/sps-context tests), build, verify:note-index,
sps-smoke (13 shots).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Document in runWork that the streaming sendMessage agent path applies grounding server-side, so the desktop has no honest notes/memory/rules count to show — a chip there would be fabricated. Prevents a future 'fix' that adds a fake count. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace the two renderer brand assets (icon.png, hermes.png) and the packaged-build icons (resources/icon.png, build/icon.png, build/icon.icns) with the SUKHI shield badge. Pure asset swap — every logo reference is already abstracted behind these files, so no JSX/logic changes. - icon.png upscaled to 1024 from the 256px source badge - hermes.png wordmark slot now shows the square badge (height 30) - build/icon.ico left as-is (no ImageMagick to regenerate cleanly) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…panel shut
The Outline/Comments/Assistant controls felt unreliable ('lots of clicking')
because two systems fought each other: the top-bar buttons TOGGLED the panel
closed when clicked on their already-active tab, while the panel tabs only
SELECTED. Same button sometimes opened, sometimes closed.
- Top-bar Comments/Assistant buttons now always open+select their tab; closing
the panel is the dedicated X button's / ⌘J's job only.
- Right-panel tabs route through openPanelTab so a click always keeps the panel
open and selects (defensive).
- Persist the active right tab (localStorage) so Outline/Comments/Info survive a
reload instead of snapping back to Assistant.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Add a visible 'Hide sidebar' collapse handle in the rail header. The collapse capability already existed (data-rail=hidden) but had no discoverable trigger; the only way back was a top-bar button after the fact. - Remove the Inbox nav item: it showed a hardcoded '3' unread badge but just opened the Search palette — a label that lied about both its count and action. - Remove the three 'Coming soon' disabled filter chips (Created by / In / Filter) from the command palette; keep the working 'Title only' chip. - De-seed single-user identity: demo fallback user 'Maya Rao' -> 'You', and the apps section label 'Notion apps' -> 'Apps' (this isn't Notion). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The app shipped as a fake team demo ("Team Home", "4 contributors", Maya/Theo/
Priya/Sam, a seeded Theo K comment, "Hi Maya" greeting), which made a one-person
app feel like someone else's workspace.
- Rewrite the Home doc + page tree as a personal agent cockpit (Home, Projects,
Reading list) with onboarding-flavored to-dos instead of a product-team sprint.
- PEOPLE table -> single built-in person "You" (PersonKey stays a free string, so
assignees/@mentions can still grow); reassign all sample tasks to "you".
- Assistant greeting drops "Hi Maya"; mock canned responses no longer reference a
fictional team/release.
- DocHeader drops the hardcoded "4 contributors"; InfoPane owner -> You, team-only
Contributors section removed; reply author -> You; ship zero seed comments.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Comments was team-collaboration framing (threads you resolve, replies from other people) in a one-person app. Reframe the surface as private margin notes you pin to text — same anchored-thread data model, single-user language: - Right-panel tab + top-bar title: Comments -> Notes - Empty state: 'pin a private note. Your assistant can read these.' - Thread input placeholder Reply… -> Add a note…; Resolve/Re-open -> Archive/Restore - Selection toolbar button title Comment -> Add note Internal data-model name stays 'comments' (persisted tab value, block wiring, tests) — only user-facing strings change. Phase 2b wires these notes into the assistant's context so the agent can actually read/act on them. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Completes the Comments->Notes repurpose: private annotations you pin to text are now fed to the agent as authoritative intent, so it can act on them. - PageContext gains optional notes[]; runAgent gathers the current page's unarchived annotations (quote + body) and passes them through. - spsAssistant IPC payload carries notes (preload .ts + .d.ts in parity); main's buildSpsAssistantMessages injects a 'Your notes on this page' section into the user turn (omitted when empty), placed after page content and before the request. - Pinned-note count folds into the trust chip's existing 'notes' tally, so the 'what it used' chip reflects them honestly. - Tests: notes injected when present, section omitted when empty. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The 11 built-in templates were hardcoded; you couldn't make your own. Now you can
save any page's structure as a reusable template.
- New templates store slice (localStorage 'sps-agent-templates-v1', mirroring the
tweaks/sidebar convenience-persistence pattern): saveAsTemplate snapshots a
page's blocks + icon/title; removeUserTemplate drops one.
- PageMenu ('…') gains 'Save as template'.
- TemplatesModal shows a 'Your templates' section above the built-ins; each card
instantiates with FRESH block ids (so two pages from one template never share
block identity) and has a hover-reveal delete button.
- Templates are a UI recipe layer, not workspace data — the PAGES they create are
still markdown-on-disk. Tests cover round-trip, malformed-entry filtering,
snapshot, empty-page guard, and removal.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Callouts and toggles already existed and are in the slash menu; the missing layout primitive was columns. Adds a 2–3 column block so you can build your own side-by-side visual setups (pros/cons, before/after, note + checklist). - New 'columns' block type with a columns[] field (HTML per column). It is NOT in the serializer's cleanTypes, so it round-trips losslessly through the existing Tier-2 <!-- sps:… --> meta comment — ZERO serializer changes, proven by a new golden round-trip test. - ColumnsBlock renders independent contentEditable columns using Editable's cursor-safe seed-once pattern; sanitises on display, plain-text paste only; hover controls to add (max 3) / remove (min 1) a column. - Slash menu gains 'Columns'; insert seeds two empty columns. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A new 'Cockpit' surface (sidebar, below Home): an at-a-glance dashboard you build yourself. Delivers the 'build my own visual setup' ask without a heavy dependency — a CSS grid + HTML5 drag, no react-grid-layout. - New cockpit store slice (localStorage 'sps-agent-cockpit-v1', matching the tweaks/sidebar/templates convenience-persistence pattern rather than the workspace.json substrate): ordered widgets, each 1× or 2× wide; reorder, resize, add, remove, reset. - Five widgets, all reading live store state: Quick actions, At a glance (page/ note/template counts), Pinned notes (jump to the annotated page), Jump to a page, and Ask your assistant (starts a grounded chat). - Drag a card to rearrange, toggle 1×/2× width, remove, or add from the catalog. - Tests: reorder, span, add/remove, persistence round-trip + malformed-input hardening (unknown kind dropped, span clamped). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
At large window sizes the right panel floated with a dead band to its right and its close button was clipped off-screen (and sat under the floating admin gear), so the panel couldn't be reliably turned off. - .main now flex:1 so it grows and the panel sits flush against the window's right edge — verified at 1900px: panel.right === window width, zero gap (was a wasted band before). - Replace the clipped in-panel close X with a dedicated panel toggle in the topbar (panelRight icon, highlighted when open) — one clear on/off control. Verified it hides and reopens the panel. - Reserve topbar right-padding when the panel is closed so its controls clear the floating admin gear (a pre-existing collision). - Tighten rp-tab padding (11px→8px) so the four tabs never overflow the panel. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…e float Topbar had three panel controls (Notes button, Assistant button, toggle) plus the panel's own tabs, and 'Assistant' was labelled twice (topbar button + panel tab). The Settings gear floated fixed over the top-right corner, overlapping the panel, and duplicated the sidebar-foot Tweaks gear. - Topbar: drop the Notes and Assistant buttons; keep one icon-only panel toggle. 'Assistant' now appears once (the panel tab). Panel controls: 3 -> 1. - Relocate settings into the sidebar 'More' section as labelled nav items — 'Appearance' (was the foot gear → Tweaks) and 'Settings' (Hermes admin). Remove the floating .sps-admin-gear button and the redundant foot gear. - Wire the parent to listen for the 'hermes:open-settings' event (previously dispatched by SPS but unhandled — a dead dispatch); ⌘, still toggles admin. Verified at 1900px: no floating gear, topbar has 2 buttons, single panel toggle, sidebar More = Trash/Appearance/Settings. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Three new cockpit widgets, all reading live data with graceful offline fallbacks: - Recent chats: hermesAPI.listSessions → click opens the chat surface on that session (empty state offers to start one). - Today: weekday + date, with a button to open today's journal entry. - Agent status: the active Hermes profile (name · model · running/stopped) with a status dot; click → Agent Console. Offline → 'Set one up' opens settings. Added to the widget catalog and the default cockpit layout; existing layouts get them via 'Add widget'. cockpit slice ALL_KINDS extended (validates persisted layouts against the new kinds). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Each column is now its own block list (headings, lists, todos, quotes, code,
dividers) instead of a single rich-text region. block.columns becomes Block[][].
- New ColumnEditor (in ColumnsBlock) reuses BlockInner for rendering and the page
editor's keyboard grammar (Enter carries list/todo type, Backspace de-indents
then unwraps then merges, Tab indents) plus markdown shortcuts via detectMarkdown
('- ', '# ', '[] ', '> ', '```', '--- '). No slash menu, so a column can't nest
another columns/database/media block — no recursion.
- Each column always keeps >=1 block; normalizeColumns tolerates the legacy
string[] shape (migrates a rich-text column to one paragraph).
- Still serializes losslessly via the generic Tier-2 path (NOT in cleanTypes);
golden test updated to a nested-block columns block and passes.
Verified at 1500px (real Electron): 2 columns render an h3 + bullets beside an h3
+ checked/unchecked todos. Full gate green (typecheck, lint, 1396 tests).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The floating admin gear button was removed when settings moved into the sidebar 'More' section; its CSS was left behind. Sweep the now-unused rules (the .sps-admin-overlay it opened stays).
On macOS the renderer paints a .drag-region (position:fixed, top:0, height:28px, z-index:1000) so the frameless window can be dragged. The SPS topbar sits in that strip, so its controls (the panel toggle, page menu, breadcrumbs) fell UNDER the drag layer and clicks hit the drag-region instead of the button — the panel toggle 'did not work'. Fix: lift .topbar above the drag layer (position:relative; z-index:1001), make the topbar's own empty space the window-drag handle (-webkit-app-region: drag), and opt the interactive children (button/.seg/.crumb) back out with no-drag. Verified in real Electron at 1500px: elementFromPoint over the toggle is now the button, and a real hit-tested click closes AND reopens the panel (previously the click timed out — only a synthetic JS click worked, which had masked the bug).
Same drag-region trap as the topbar (fixed in 84ffe3e): the panel tab row (.rp-tabs: Assistant/Outline/Notes/Info) sits in the top 28px window-drag strip (.drag-region, fixed, z-index 1000), so clicks on the tabs hit the drag layer instead. Fix: lift .rp-tabs above the drag layer (position:relative; z-index:1001), keep its empty space a window-drag handle (-webkit-app-region: drag), and opt the tab buttons back out (no-drag). Found via a full-surface click audit (elementFromPoint over every interactive control on every surface). Verified in real Electron: rp-tab is the top element and a real click on 'Outline' switches the active tab. (The sidebar Appearance/Settings items sit below the scroll fold on short windows but ARE reachable by scrolling — clickable, not covered — so left as-is.)
Settings sat at the bottom of the scrollable nav, below the fold on short windows. Move it to a gear pinned in the rail foot (next to the profile), always visible without scrolling. Removed the duplicate Settings item from the 'More' section; Appearance stays there. Foot is below the macOS drag strip, so no app-region issue. Verified in real Electron: foot gear is clickable and a real click opens the Hermes admin overlay; Settings no longer appears in the scrollable nav.
…hidden When the sidebar is collapsed, the topbar reaches the window's left edge — where the macOS traffic-light buttons sit (trafficLightPosition x:16). The breadcrumb + show- sidebar control rendered underneath them. Inset the topbar by 78px on mac when data-rail=hidden so they clear the lights (mirrors the existing rail-top margin-top clearance for the visible-sidebar case). Verified in real Electron (sidebar hidden): topbar padding-left 78px, show-sidebar button at x:78 and breadcrumb at x:113 — both clear of the x:16-70 light zone.
The Settings overlay mounts the legacy Hermes Layout, whose sidebar still listed 'SPS Agent' and 'Chat' workspace views (WORKSPACE_NAV_ITEMS). Clicking 'SPS Agent' there rendered a SECOND full copy of the SPS app inside the overlay, beside a nav that duplicated the SPS sidebar (Insights, Memory, Chat, …). Since SPS replaced the Layout as the main app, those workspace entries are pure redundancy. Remove WORKSPACE_NAV_ITEMS (SPS Agent / Chat) and the spsAgent content pane so the overlay is a focused admin panel (Agent Control Center: Sessions/Profiles/Office/ Kanban/Insights/Models/Providers/Skills/Memory/Personalization/Tools/Schedules/ Gateway/Settings). Drop the now-unused SpsAgent/Sparkles/ChatBubble imports. Verified in real Electron: opening Settings shows only admin nav (no SPS Agent/Chat item) and exactly one SPS rail in the DOM (no embedded second app).
- Rename the Hermes admin-overlay 'Insights'/'Memory' to 'Agent Insights'/'Agent Memory' so they no longer read as duplicates of the SPS workspace's own Insights/ Memory surfaces (different layer, same words). - Pin Appearance to the sidebar foot next to Settings (both always visible, no scrolling); remove it from the scrollable 'More' section (now just Trash). Verified in real Electron: foot has Appearance+Settings gears (Appearance gone from the nav, clickable), admin overlay shows Agent Insights/Agent Memory (plain labels gone). Also smoke-audited all 14 admin screens — every one mounts and renders with no console/page errors.
Dark mode gets a 'Dark palette' picker (Appearance): Black, Warm, Terminal. - Black (NEW DEFAULT): near-black #0a0a0a bg + off-white #e8e8e8 text, tuned off-pure to avoid white-on-black halation while keeping subtle elevation (surface #161616) so cards/panels/hover don't flatten. - Warm: the original gold-tinted dark (unchanged base [data-theme=dark]). - Terminal: shares the black palette but paints the DOCUMENT text terminal-green (#3fb950 body, #4ade80 headings); chrome (sidebar/menus) stays neutral so the app stays scannable. Accent stays gold across all three. Implemented as a data-skin variant layer over [data-theme=dark] — pure attribute swap, no JS color math; mirrored to documentElement like data-theme. darkSkin added to Tweaks (default 'black'); existing persisted tweaks merge to the black default. Verified in real Electron: default loads data-skin=black (rail bg #0a0a0a); terminal gives green doc text + neutral chrome (#a6a6a6); picker shows only in dark mode.
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.
Summary
Test Plan