Tags: RoseAgent/ProjectRose
Tags
Refactor/architecture (#75) * Split llmClient god-module into focused modules llmClient.ts had grown to 1137 lines mixing five unrelated concerns. Extract three of them, leaving llmClient.ts owning only the streaming loop: - modelResolution.ts — resolveModel + projectrose SSE-patch fetch - coreTools.ts — buildCoreTools (the 445-line tool catalog) - contextCompression.ts — compressTurnsForContext + turn-splitting helpers Pure move: all four function bodies are byte-identical to the originals; importers (index.ts, aiService.ts) repointed at the new modules. No cycle — buildCoreTools is still injected via toolRegistry.registerCoreTools. typecheck clean; 251 unit tests pass; app builds and boots. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Extract calendar invite-and-merge into a shared service The "read event → verify Google-synced → send invite → merge attendees locally" sequence was duplicated in two layers: the agent tool handler (handleCalendarInviteToEvent) and the renderer IPC handler (inviteToEvent), which also meant business logic lived inline in the IPC registration block. Move it to a single leaf service, memory/calendarInvite.ts, returning a discriminated EventInviteOutcome so each caller keeps its own presentation (the tool returns prefixed strings; the IPC returns GoogleApplyResult) with zero change to either's user-facing messages. Lives in its own module rather than calendar.ts to avoid a cycle (googleCalendar.ts already imports calendar). typecheck clean; 251 unit tests pass; all three bundles build. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Add characterization tests for routine/channel-rule field parsers These two on-disk markdown parsers had no test coverage. Lock their current parse/build/round-trip behavior (tools inline + sub-list, unknown-bullet preservation, boolean variants, RRULE normalization, source coercion, section ordering, slugify) before extracting their shared scaffolding. 19 tests, all green against current code. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Extract shared rule-document scaffolding for routine/channel parsers routineFields.ts and channelRuleFields.ts implemented the same on-disk markdown skeleton twice — line-walking, the tools inline/sub-list state machine, unknown-bullet preservation, the ## section walk, the builder tail, parseBoolean, and slugify were byte-near-identical (the channel parser's header even reads "Mirrors routineFields.ts"). A parse fix had to be applied in both, inviting silent divergence. Move the skeleton to shared/ruleDocument.ts (parseRuleDocument / buildRuleDocument / parseBoolean / slugify). Each parser now only maps the generic labeled bullets to its typed fields and supplies its ordered metadata bullets on build, keeping its domain-specific coercion (RRULE normalization, source enum). routineFields 250->151, channelRuleFields 234->135. Behavior preserved: the 19 characterization tests added in the prior commit stay green. typecheck clean; 270 tests pass; all three bundles build. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Add agent tools for managing routines from chat; bump to 1.9.4 rose-routines now registers eight routines_* extension tools (list, read, create, update, delete, run_now, list_runs, read_run) following the rose-channels pattern: declared in both manifests, registered via ctx.registerTools() so the strict catalog reconciliation holds. Writes go through saveRoutine/deleteRoutine so every change reschedules the timer and broadcasts routines:changed like a UI edit. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Fix path traversal in routines tools; restore calendar invite error precedence Review fixes from /code-review: - Reject slugs/filenames with separators or dot segments at the rose-routines path-building chokepoints, closing arbitrary read/overwrite/delete via agent-supplied ../ values - Validate run filenames end in .md and surface bad-slug errors instead of swallowing them in try/catch - Check no-event/not-synced before empty attendees in the shared invite service so the agent sees the real blocker first - Clear a routine's schedule on empty recurrence instead of writing the malformed rule "RRULE:" - Share normaliseRrule from routineFields instead of mirroring it - Move the duplicate-slug guard into createRoutine so every caller is protected, not just the agent tool Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Pin Windows release runner to windows-2022 (VS2026 breaks node-gyp) GitHub migrated windows-latest to the windows-2025/VS2026 image. The bundled node-gyp 9.4.1 only detects VS 2017/2019/2022, so it fails with "Could not find any Visual Studio installation" when rebuilding native node-pty during packaging. Pin to windows-2022 (VS 2022) to restore the working toolchain. Also set fail-fast: false so one platform's failure no longer cancels the others, giving the full per-OS signal on every run. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add missing rose-channels, detached-run-transcript, and email-sync-lo… …op sources The previous commit (8e1265f) imported these new modules but never staged the files, breaking CI typecheck/tests and the release build with ERR_MODULE_NOT_FOUND. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Remove playwrite tests (They have become to dated
[PRD] ipc-pass-through (#74) * Introduce typed IPC manifest and migrate chat sessions (#39) Add defineIpc(namespace, methods) which produces a register() function that wires ipcMain.handle for each method, a typed bindings object that wraps ipcRenderer.invoke, and inferred types for both sides. Channel strings are derived as namespace:method so the wire format is unchanged. Migrate the chat sessions namespace as the tracer slice: the four SESSION_* enum entries, sessionHandlers.ts, and the verbose preload session block are all replaced by a single sessionService.ipc.ts sibling and a registerIpcManifests() switchboard that coexists with the existing registerAllHandlers(). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Migrate prompts to the typed IPC manifest (#40) Move the prompt logic from src/main/ipc/promptHandlers.ts into a new promptService.ts and declare the six request methods through a sibling promptService.ipc.ts. agentMd repoints loadExtensionPrompts to the service module; the six PROMPTS_* enum entries are removed. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Migrate skills list/delete to the IPC manifest, keep upload (#41) skills.list and skills.delete move to a skillService.ipc.ts sibling and a new deleteSkill() service function. SKILLS_UPLOAD stays as a hand-written handler because it opens a file dialog anchored to the calling BrowserWindow. preload spreads the manifest bindings and adds upload on top — that merge pattern is the template later mixed namespaces (extensions, auth) will copy. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Migrate files namespace to the IPC manifest (#42) The eight FILE_* channels become fileService.ipc.ts. preload re-wraps the manifest bindings into the existing flat api.readFile / writeFile / ... surface so renderer call sites are unchanged. fileHandlers.ts is deleted and the FILE_* enum entries are gone. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Migrate recent projects to the IPC manifest, isolate APP_QUIT (#43) The four PROJECTS_* handlers move to recentProjects.ipc.ts. The inline `app.getPath('home')` for the default path is factored into recentProjects.getDefaultProjectPath(). APP_QUIT becomes a one-method appHandlers.ts — the manifest is for service surfaces, not Electron singleton wrappers. projectHandlers.ts is deleted. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Migrate settings to a service module and IPC manifest (#44) settingsHandlers moves to services/settingsService.ts (its business logic — readSettings, writeSettings, sensitive-field tracking — never belonged in src/main/ipc). Two manifests are declared: `settings.get/set` and a single-method `health.checkAll`. Every caller of the old ipc/settingsHandlers path is repointed to the service location. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Migrate project settings and tools list to the IPC manifest (#45) projectSettingsHandlers moves to services/projectSettingsService.ts. Two manifests are declared: `project.getSettings/setSettings` and a single-method `tools.list` that wraps the core+extension merge. The three hard-coded channel strings ('project:getSettings', 'project:setSettings', 'tools:list') disappear — namespaces derive them now. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Extract rose setup service and migrate to the IPC manifest (#46) roseSetupHandlers becomes services/roseSetupService.ts with named exports checkRoseMd, initRoseProject, ensureRoseScaffold and the personality constants / buildRoseMd helper. The three ROSE_* enum entries are replaced by a `rose` namespace manifest. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Migrate whisper transcription to the IPC manifest (#47) The whisper handler body — read settings, set model, transcribe — becomes whisperService.transcribeAudio. A single-method `whisper` manifest wraps it. The vestigial saveChatRecording stub is dropped. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Migrate updater request channels to the IPC manifest (#48) The four request channels (check, download, install, skipVersion) move to updaterService.ipc.ts. The five event-broadcast channels stay as IPC enum entries because they're emitted via webContents.send and the manifest covers ipcRenderer.invoke round-trips only. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Migrate auth request channels to the IPC manifest (#49) The five AUTH_* request handlers move to a new authService.ipc.ts; the underlying auth code is also relocated from main/lib/authHandler to main/services/authService for consistency. The login handler's error-rewrap wrapper survives as loginViaAuthWindow(). AUTH_CHANGED and AUTH_PAIRING_PENDING stay as event-broadcast enum entries. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Migrate AI request channels to the IPC manifest (#50) The seven AI_* request channels (chat, contextStatus, compressToolNoise, getSystemPrompt, cancel, askUserResponse, captureScreenshotResult) move to services/aiService.ipc.ts. preload re-wraps positional renderer signatures into the manifest's payload-style bindings so the api shape stays unchanged. The ten event-broadcast AI_* entries remain in the IPC enum. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Migrate active speech to the IPC manifest (#51) The 11 ACTIVE_LISTENING_* invoke channels move to a new activeSpeechService manifest. The training orchestration (with its jobPhases map and async setImmediate flow) is factored into startTrainingJob(). The trimmed activeSpeechHandlers.ts keeps just the two fire-and-forget ipcMain.on registrations (SEND_CHUNK, CANCEL_DRAFT) and the one-time speaker-cache init. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Migrate extensions to the IPC manifest (#52) extensionHandlers.ts (730 lines) becomes services/extensionService.ts. Every previously-inline handler body is factored into a named export (listExtensions, installFromGit, installFromDisk, installPreviewFromGit/Disk, installConfirm/Cancel, uninstallExtension, enableExtension, disableExtension, loadRendererCode, loadMainModule). The 12 EXTENSION_* enum entries and the dedicated handler file are gone; the sibling extensionService.ipc.ts declares the namespace. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Document the IPC channel policy after the manifest migration (#53) Add a header to ipcChannels.ts spelling out what belongs in the enum (event broadcasts and kept-back hand-written handlers) and what does not (service request/response channels — those are derived from defineIpc manifests). Tighten the comments on the two register functions in main/ipc/index.ts now that the migration coexistence is done. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Repoint chatSessionRun.test.ts mocks at the new service modules The three vi.mock() calls still pointed at ../../ipc/settingsHandlers, projectSettingsHandlers, and extensionHandlers — the files that the manifest migration moved to ../settingsService, ../projectSettingsService, and ../extensionService. On Linux CI the stale mocks didn't apply, so listInstalledExtensions ran for real and tried to mkdir /proj. Locally on Windows the same call succeeded against C:\proj, hiding the issue. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Stop the LSP from crashing the app on Finder-launched macOS (#73) The "tons of pop up errors" reported after the previous Mac PR all trace back to `spawn node ENOENT` from the LSP manager. When a macOS `.app` is launched from Finder (or any GUI), the OS hands it a minimal PATH of `/usr/bin:/bin:/usr/sbin:/sbin` — none of /opt/homebrew/bin, /usr/local/bin, NVM dirs, etc. So `spawn('node', ...)` fails for every user whose node lives there (i.e. most of them). The async ENOENT from spawn had no `'error'` listener either, so it bubbled up as an uncaughtException → Electron's default modal-dialog popup, twice (py + ts servers). Three fixes plus a safety net: 1. `lspManager.ts`: stop relying on a system `node`. Use `process.execPath` with `ELECTRON_RUN_AS_NODE=1` so the LSP scripts run under Electron's bundled Node — the same pattern VS Code uses. Also add a `proc.on('error')` listener so future spawn failures log instead of crashing. 2. New `src/main/lib/childProcessEnv.ts` — small helper that prepends the usual Mac install locations (homebrew, ~/.bun, ~/.cargo, ~/.local/bin, ~/.volta) to PATH for child processes. Used by: - `extensionHandlers.runCommand` (extension installer's `npm install` was also broken — `npm` isn't on the launchd PATH). - `toolHandlers.handleRunCommand` (AI-invoked shell commands need to be able to find user-installed tools). 3. `index.ts`: register `process.on('uncaughtException')` and `process.on('unhandledRejection')` handlers that log via electron-log instead of letting Electron pop a modal dialog. Future surprises don't get to spam the user with un-dismissable popups. 4. `extensionHandlers.runCommand`: include a clearer "X is not on PATH" hint in the ENOENT branch so users can self-diagnose missing binaries instead of seeing a raw stack trace. Verified by running the packaged app binary with `env -i PATH=/usr/bin:/bin:/usr/sbin:/sbin` (faithfully reproducing what `launchd` hands a Finder-launched app). Before this commit: two "Uncaught Exception" modal dialogs and no LSP. After: both pyright and typescript-language-server spawn cleanly as Electron-as-node children, tsserver subprocesses launch under them, no popups, no errors in the log beyond the existing `[updater] skipping autoUpdater: dev mode` info line. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Make the unsigned macOS build launchable (#72) The DMG produced by the release workflow was effectively unusable on modern macOS: Gatekeeper blocks the unsigned `.app`, the menu bar reads "Electron" instead of "ProjectRose", the window icon is broken, the `projectrose://` scheme never reaches the renderer, and only an arm64 artifact is uploaded. None of this needs a Developer ID to fix — these are bugs in how the app and the build are configured for Darwin. Changes: - window.ts: pick `icon.png` on Mac/Linux instead of the Windows `.ico`; switch to `titleBarStyle: 'hiddenInset'` on Mac (the existing `hidden` + `titleBarOverlay` config clips the native traffic lights) and guard the theme:changed overlay refresh. - menu.ts: prepend `{ role: 'appMenu' }` on Mac so the menu bar shows the product name with the canonical About/Hide/Quit slots; move Quit off the File menu where the app menu owns it; add the standard Window submenu. - index.ts: register `app.on('open-url')` so projectrose:// links delivered to a running app reach the renderer; call `setAsDefaultProtocolClient('projectrose')` for dev mode. - tray.ts: resize the 1024x1024 source to 22x22 and mark it a template image so the menu-bar icon adapts to light/dark mode. - ipcChannels.ts: add DEEPLINK_RECEIVED for the open-url payload. - package.json `build.mac`: target both arm64 and x64 DMGs (Intel users got nothing before); switch icon to `.icns`; add Info.plist entries for the URL scheme and the mic/camera usage descriptions (without the usage strings the first mic prompt crashes the process). - release.yml: generate `build/icon.icns` via `iconutil` on the Mac runner and upload both DMG artifacts. - README.md: document the one-time `xattr -dr com.apple.quarantine` step users need until a Developer ID is in place. Auto-updater is already correctly disabled on Darwin (updaterService.ts:45). Signing/notarization stays out of scope until an Apple Developer ID exists — the new `build.mac` block leaves `identity`/`hardenedRuntime`/`notarize` unset, which makes electron-builder skip signing entirely. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
[PRD] chat-turn-unification (#71) * chat-turn-unification: forward sessionId on streaming events (#2) * chat-turn-unification: route AI_CANCEL by sessionId (#3) * chat-turn-unification: ChatSession.run owns the chat loop (#4) * chat-turn-unification: one-shot role on ChatSession; main vs subagent parity (#5) * chat-turn-unification: useChat slice mirrors the four legacy stores (#6) * chat-turn-unification: migrate chat input and timeline consumers to useChat (#7) * chat-turn-unification: migrate sessions and compression consumers to useChat (#8) * chat-turn-unification: fold empty response and defer into useChat settle (#9) * chat-turn-unification: delete legacy renderer modules; useChat owns all state (#10) * chat-turn-unification: multi-session concurrency smoke test (#11)
PreviousNext