Add Anthropic Claude OAuth support and enhance Factory tab features#720
Add Anthropic Claude OAuth support and enhance Factory tab features#720Avi-Bendetsky wants to merge 41 commits into
Conversation
…lans Adds an 'Anthropic Claude (OAuth)' card to the OAuth Plans panel with a native paste-a-code PKCE sign-in flow. The existing OAuth handler spawns 'hermes auth add <provider> --type oauth' with stdin ignored and only auto-handles device-code flows. Anthropic uses a paste-a-code PKCE flow that needs the code fed back, which the CLI refuses on a non-tty stdin. This implements the PKCE dance natively in the Electron main process and persists the resulting token into the engine credential pool, so repeated sign-ins add multiple Claude accounts. - constants.ts: add anthropic OAuth card - hermes-auth.ts: buildAnthropicAuthUrl + exchangeAnthropicCode (native PKCE, dual token endpoint, pool persistence via engine) - index.ts: anthropic-oauth-start / -submit IPC handlers - preload: anthropicOauthStart / anthropicOauthSubmit bridge - OAuthLoginModal.tsx: paste-code UI branch for anthropic - i18n: anthropicDesc (all locales) + common.submit Verified: typecheck clean; PKCE challenge=S256(verifier); token exchange reaches both endpoints and is correctly rejected on a bad code.
Add Anthropic Claude (OAuth) to Subscription/OAuth Plans
Addresses Codex review: the Anthropic submit path didn't pass the selected profile, so credentials always landed in the default profile's pool. Now profile flows renderer → preload → IPC → exchangeAnthropicCode → persistAnthropicToken, which points HERMES_HOME at the profile home (<HERMES_HOME>/profiles/<name>) for non-default profiles. Default profile behavior unchanged.
fix: thread selected profile through Anthropic OAuth persistence
Surface the engine's existing multi-account credential rotation in the Providers UI. Each OAuth sign-in already appends a new pool entry that the engine rotates across (skipping rate-limited ones), but nothing in the UI communicated this. - OAuthLoginModal: add an onSuccess callback fired when an account is successfully added, so the Providers screen can reload the pool immediately. Previously a new account stayed invisible until the user navigated away and back (onClose did not reload). - Providers OAuth card: show an "N connected" badge and an "Add account" button (instead of "Sign in") once a provider has >=1 account, plus a hint explaining automatic rotation. Generic across all OAuth providers. - Credential-pool list: per-account Active / Rate-limited / Failed badges computed from the engine selection strategy and last_status, so users can see which account is live and which are exhausted. - i18n (en): addAccount, accountsConnected (plural), rotationHint, and the pool status badge/hint strings. Other locales fall back to en. Motivation: adding multiple Anthropic accounts is the fix for hitting Claude Pro/Max rate limits - this makes that capability discoverable. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A local TLS-intercepting proxy (corporate MITM, the 9Router dev proxy, Fiddler/mitmproxy) presents a self-signed root CA that the Python gateway's bundled certifi store does not trust. Electron itself is unaffected because it launches with NODE_EXTRA_CA_CERTS, but the gateway is a separate Python process whose httpx/requests/SDK calls fail with CERTIFICATE_VERIFY_FAILED against every upstream (Anthropic, Gemini, MCP servers). buildGatewayEnv now detects such a proxy CA (NODE_EXTRA_CA_CERTS or the 9Router rootCA), builds a combined bundle (certifi + the proxy CA) under HERMES_HOME, and points HERMES_CA_BUNDLE / SSL_CERT_FILE / REQUESTS_CA_BUNDLE / CURL_CA_BUNDLE at it. No-op when no proxy CA is present or when the user already set those vars, so default installs are unaffected. The bundle is regenerated only when missing or stale. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… in the UI
Surfaces the autonomous dev-factory's engine-side governance (which until now
lived only in config.yaml + CLI) as a first-class Hermes One sidebar tab,
between Kanban and Models.
Four sections, editable:
- GOVERNANCE: oversight level selector (monitor/warn/gate/strict, factory-wide),
secret-scan enable/disable (shows the live pattern count: 23 + entropy),
a per-profile table (level/secrets/hybrid/protected), and a protected-paths
chip editor (add/remove globs).
- BUDGET: kill-switch halt/resume + the breaker dimensions.
- ORCHESTRATION: the kanban orchestrator/assignee/auto-decompose knobs (read).
- ACTIVITY: the live governance-block feed (decision/code/path) + recent builds
— the proof the governor is actually firing.
Data path mirrors the existing Kanban tab: renderer ->
window.hermesAPI.kanbanGovern{Status,Set,KillSwitch} -> IPC -> main/kanban.ts
-> execFile(hermes, ["kanban","govern","--json"]). The engine command does all
config reads/writes (surgical, comment-preserving); the UI is a thin client.
Files: main/kanban.ts (govern bridge fns), main/index.ts (3 IPC handlers),
preload/index.ts + .d.ts (api + GovernStatus/GovernSetChange types),
screens/Factory/Factory.tsx (new), Layout.tsx (View+NAV_ITEMS+pane),
icons (ShieldCheck), en/navigation.ts (factory key), main.css (factory-* styles).
Verified: npm run typecheck (node+web) exit 0; electron-vite build exit 0.
Remote-only mode shows the RemoteNotice (governance needs local/SSH).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Implements the agreed PRD on top of the v2 engine (autonomy semantics + govern extensions, hermes-agent 4c464f7f7). LAYOUT (Nielsen-applied): - Pinned status strip: oversight level (+MIXED badge), Running/HALTED, secrets+pattern count, hybrid X/6, blocks-today. - User-selectable section order (localStorage): Control-first / Monitoring-first / Classic. NEW CONTROLS: - Per-profile table now fully editable inline: level dropdown, secrets toggle, hybrid toggle — instant-save via govern set --for-profile. Factory-wide level buttons + Enable/Disable-all remain as bulk shortcuts. - Budget: default max-iterations + wall-clock inputs (0=unlimited, inherited by new cards), shows the per-block retry cap. Kill-switch relabeled "Halt all agents". - Orchestration: read-only knob table. ACTIVITY: - Auto-refresh (15s, toggleable, pauses when tab hidden). - Filters: decision / code / profile. - Columns: when, decision, code, profile, finding message. - Click a block row -> jump to the Kanban tab (v1 cross-tab; task-focus is a fast-follow). - Settings change-log panel (from the engine's reversible changelog). BEHAVIOR (Nielsen #3/#5/#9): - Confirm dialogs before the 4 risky actions: Halt all agents, Disable secret scanning, Set level to monitor, Remove a protected path. - Undo toast on changes (wired for level changes — captures prior value and re-applies; other changes toast-only). - Failed writes surface the engine's actual error. IPC: governSet gains hybrid/defaultMaxIterations/defaultWallclock; GovernStatus type gains budget.default_*/per_block_retry_cap + activity.change_log. The bridge already uses --for-profile (not --profile) to dodge hermes's global -p. Verified: npm run typecheck (node+web) exit 0; electron-vite build exit 0. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
GOVERNANCE table: new Model column — a per-agent LLM picker. Dropdown is the engine's curated cc/ + ag/ list (governModels → 9Router /v1/models, filtered to adapter-compatible shapes; fail-soft to configured models). A "+ custom…" option prompts for any id; a cx/ id is warned in the UI and hard-rejected by the engine (govern set --model). Instant-save with an undo toast. ORCHESTRATION section is now editable: - Orchestrator profile + Default assignee: dropdowns over the 6 profiles. - Auto-decompose: on/off toggle. - Auto-decompose per-tick + Max in-progress / profile: number inputs. - failure_limit + dispatch_in_gateway stay read-only (lower-level, by design). All instant-save via govern set, change-logged. IPC: new kanbanGovernModels (kanban-govern-models); governSet gains model, autoDecomposePerTick, maxInProgress. GovernProfileState gains `model`. Verified: npm run typecheck (node+web) exit 0; electron-vite build exit 0. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…loop controls Surfaces the engine closed-loop (verify→correct→done) in the Factory tab. BUILDS section (the "oversees what they're doing" pane): per-build cards showing goal/title (click-through to the Kanban task), loop-state badge (Building / Verifying / Correcting / Done / Escalated), orchestrator profile, verify round N/max, last verdict (PASS green / FAIL red), collapsible acceptance criteria, and on escalation the "why escalated" diagnosis. Empty + loop-off states explained. Added to all three layout orders. ORCHESTRATION gains a "Closed-loop oversight" group: - Orchestrator loop on/off toggle — turning ON is confirmed (it changes how builds complete); turning OFF is instant. - Max verify rounds input (1–10, default 3). Bridge/types: GovernSetChange + governSet gain orchestratorLoop + maxVerifyRounds (--orchestrator-loop / --max-verify-rounds). GovernStatus gains builds: GovernBuild[] and orchestration.orchestrator_loop / max_verify_rounds. New GovernBuild interface mirrored in preload .d.ts and Factory.tsx. Verified: npm run typecheck (node+web) exit 0; electron-vite build exit 0. Engine side (044bc79a3, e591bb13d) already shipped + loop OFF by default, so this UI shows an empty/off state until the user enables the loop. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Previously clicking a build in the Factory Builds pane only switched to the
Kanban tab. Now it opens that build's task detail.
- Kanban gains focusTaskId + onFocusHandled props; an effect opens the task
detail (setDetailTaskId) once the screen is visible, then notifies the parent
to clear the one-shot request (so re-clicking the same task re-focuses).
- Layout holds kanbanFocusTaskId; focusKanbanTask(taskId) sets it + goTo('kanban')
(which also marks the view visited so the pane mounts and the effect fires).
Factory's onNavigateToTask now routes through it instead of a bare goTo.
Verified: typecheck (node+web) exit 0; electron-vite build exit 0.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
feat(factory): Factory tab — governance/budget/orchestration + orchestrator closed-loop UI
Adds a toolbar toggle inside a chat session that activates "factory mode" (enables the orchestrator closed-loop) AND opens a live side panel showing what the factory is doing — active builds, loop state, verify rounds, escalations — without leaving the chat for the Factory tab. Renderer-only; every engine capability + IPC already exists (kanbanGovernStatus / kanbanGovernSet). - useFactoryStatus hook: polls kanbanGovernStatus every 15s ONLY while the panel is open; setLoop flips kanban.orchestrator_loop optimistically (revert + error on failure). Mirrors the Factory tab's load/poll + reuses its loop-state read. - FactoryPanel: pure-presentational right-hand column (mirrors WorktreePanel placement). Chat owns the single hook instance and passes data down, so the toggle and panel share one source of truth (no double polling). Reuses the Factory tab's build-card / chip / loop-state vocabulary. Remote/loading/error guards. Carries an explicit "Factory mode ON/OFF" switch. - FactoryToggle: toolbar chip (ShieldCheck) in Chat's toolbarExtras, beside the model/reasoning pickers. Hidden in remote mode. Opening it enables the loop if it's off; closing the panel leaves the loop AS-IS (closing a viewer must not silently kill an in-flight autonomous build) — matching the engine's loop-off-by-default safety posture. - Lifted the duplicated GovernStatus/GovernBuild interfaces out of Factory.tsx into screens/Factory/types.ts (single source of truth; the hook + panel import them). - i18n: chat.factory.* keys in en + es + zh-CN. typecheck + build clean. No new test failures (the 15 in hermes-api / hermes-cli-session-id are pre-existing on main — a getConfigValue mock gap). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
feat(chat): in-chat Factory toggle + live Factory panel
# Conflicts: # src/main/hermes.ts # src/renderer/src/screens/Chat/Chat.tsx # src/renderer/src/screens/Providers/Providers.tsx
Upstream's auto-updater (publish → fathah/hermes-desktop) overwrote the installed fork build (0.5.8, with the Factory tab + in-chat panel) with upstream 0.6.1, which also fails to launch, and deleted the local backups. - Version 0.6.1 → 0.6.2 so the fork build outranks upstream's latest release and won't be replaced by an upstream auto-update. - Repoint publish (electron-builder.yml) + dev-app-update.yml from fathah/hermes-desktop to BAS-More/hermes-desktop-Working-. The fork has no published releases, so the app finds no auto-update (no overwrite) until we deliberately publish one. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
One-shot, manually-triggered workflow that builds an UNSIGNED macOS .dmg on a hosted macOS runner — no Apple secrets, no notarization, no publishing. Just uploads the .dmg as a workflow artifact for download. Why: electron-builder 26.x removed cross-platform Mac builds (requires real macOS), and the existing release.yml mac job needs Apple secrets for notarization. This is the minimal recipe to produce a runnable .dmg. Caveats noted in the workflow comment: Gatekeeper blocks unsigned apps on first launch; user does right-click -> Open or 'xattr -dr com.apple.quarantine'. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
chore: merge upstream v0.6.1 + bump to 0.6.2 with auto-update feed fix
…rough, multi-profile Sessions overhaul
Three follow-ups on the v0.6.2 Factory work:
1) Factory → Office bot animation
- The Office 3D scene now consumes the same kanbanGovernStatus the in-chat
Factory panel reads (via a lifted useFactoryStatus hook in screens/shared).
- New pure `enrichOfficeAgentsWithBuilds` overlays factory state onto bots:
active builds drive the orchestrator bot to "working", parked builds
paint it red, done builds tag a celebrateUntil deadline (4s).
- AgentsLayer honours celebrateUntil inside its existing useFrame — the
responsible bot dances for ~4s when a build completes.
2) In-chat Factory panel → Kanban clickthrough
- Thread Layout's focusKanbanTask through Chat → FactoryPanel via a new
onNavigateToTask prop. The build-card link is now live; the rest of the
plumbing already existed.
3) Sessions tab overhaul (root cause: ~/.hermes/active_profile file missing
on this machine, so the tab opened the empty default state.db while 26
real sessions lived in 5 named-profile DBs)
- utils.ts: listAllStateDbPaths + stateDbPathForProfile + defensive
writeActiveProfileName (so a profile switch never leaves the file
missing again).
- sessions.ts: listAllSessions / searchAllSessions / per-profile resume
and delete; new desktop-owned tables `desktop_session_meta` (pin,
status, group) and `desktop_session_group` created lazily via
ensureDesktopSessionTables — fully additive, no engine schema change.
- session-cache.ts: per-profile sync helpers.
- profiles.ts: mirror active_profile to disk on every setActiveProfile
so the regression cannot recur.
- IPC + preload: rename-session, archive, pin, status, move-to-group,
groups CRUD, plus typings.
- Sessions.tsx: unified list across all profile DBs, profile chip on
every card, pinned section, group filter, Show archived toggle, and
a per-card ⋯ overflow menu with 10 actions: pin / pause+continue /
mark complete / rename / move-to-group (with new-group inline) /
copy link (hermes://session/<profile>/<id>) / share (OS share sheet
with copy-link fallback) / archive / delete (hard-delete via engine
path, with confirm).
- Layout.tsx: handleResumeSession now takes (sessionId, profile) and
setActiveProfile follows the resumed session so it runs under the
right gateway — fixes a quiet correctness bug.
- SidebarRecentSessions: same aggregator + profile threading.
Tests
- New unit tests for enrichOfficeAgentsWithBuilds (7/7 pass).
- Sessions.test.tsx updated to the new aggregator API + ⋯ menu interaction
(14/14 pass).
- npm run typecheck clean; npm run build exit 0.
- Full vitest: 24 pre-existing infra failures (commandProvider, config-health,
tui-gateway-stream, api-server-key, hermes-cli-session-id) — none new.
i18n: en/es/zh-CN parity for sessions.actions.*, sessions.status.*,
sessions.pinnedSection, sessions.showArchived, sessions.filterGroup,
group dialogs.
Co-Authored-By: Claude <noreply@anthropic.com>
…ions-overhaul feat: Factory→Office bots · Factory-panel clickthrough · multi-profile Sessions with 10-action menu
Outranks any upstream auto-update so the locally-installed Factory+Office+ Sessions build (PR #6) isn't silently overwritten. Co-Authored-By: Claude <noreply@anthropic.com>
Acts on a UI Designer + Accessibility Auditor + Persona Walkthrough audit of
the Sessions tab. Tier 1 (safe quick wins) + Tier 2 (high-value).
Tier 1
- Define missing --shadow-md / --shadow-lg / --danger at :root. They were
referenced by the confirm modal, the per-card overflow popover, and the
toast but defined nowhere, so all three rendered flat. One block fixes
every consumer app-wide; themes can override.
- Rename input: --bg-base (undefined → transparent) → --bg-primary.
- Overflow (⋯) button: 24→28px (comfortable above WCAG 2.5.8), rest opacity
0.45→0.7 (was near-invisible), and a real focus-visible outline (was
opacity-only).
- Add :focus-visible rings to the session card + group filter (were invisible
on dark themes — WCAG 2.4.7).
- Toast: role="status" aria-live="polite" (was silent to screen readers —
WCAG 4.1.3).
- aria-label on the search-clear button and the group-filter select; add
type="button" to clear (WCAG 4.1.2).
- Delete ~60 lines of dead CSS (.sessions-card-delete/.sessions-card-rename —
the inline icons the ⋯ menu replaced).
Tier 2
- Undo on archive + mark-complete: both removed/hid a card silently with no
recovery, re-creating the "session disappeared" anxiety this tab was built
to fix. Now each shows a toast with an Undo that reverses both the UI patch
and the engine write; longer 6s dwell when Undo is offered.
- ProfileChip: render as a filled pill (palette's intended white-on-colour
contract) instead of colour-as-text on the dark card, which failed WCAG
1.4.3 for several hues (slate ~2.0:1, purple ~2.6:1, gray ~3.2:1).
- Search results keep date grouping (today/yesterday/earlier) + a result
count instead of collapsing to a flat list, preserving temporal scent for
the most common job (re-finding "yesterday's" chat).
i18n: en/es/zh-CN — clearSearch, resultCount, actions.{undo,archived,
markedComplete}.
typecheck clean; Sessions + Office tests 21/21; npm run build exit 0.
Co-Authored-By: Claude <noreply@anthropic.com>
fix(sessions): 3-agent UI/UX audit — tokens, a11y, undo, contrast
…ps, card semantics Completes the deferred structural items from the 3-agent audit (the Critical keyboard/screen-reader barriers PR #7 left out because they needed real component work, not token swaps). 1. Focus-managed action menu (WCAG 2.1.1 / 4.1.2) - The ⋯ menu was a role="menu" that never moved focus into the popover, so keyboard users could open it but reach NONE of the 9 actions (they Tab'd behind it). Rebuilt as a proper APG menu: opening focuses the first item; ArrowUp/Down roam with roving tabindex; Home/End jump; Escape closes and returns focus to the trigger; Tab closes. ArrowDown/Enter/Space on the trigger open onto the first item. - The "move to group" flyout (position:right:100%, clipped at the viewport edge and keyboard-unreachable) is replaced by an inline expanding section in the same flat list — no flyout, linear arrow roving, no edge clipping. 2. New-group modal (replaces window.prompt) - Group creation was buried under ⋯ → Move to group ▸ → New group… and used a raw window.prompt (unstyled, breaks in Electron, no focus management). - Added a visible "New group" button in the header and a styled modal with a labeled input, Enter-to-create, focus trap. Creating also filters the list to the new group. 3. Modal focus management (WCAG 2.4.3 / 2.1.2) — new useFocusTrap hook - Both confirm dialogs + the new-group modal now trap Tab inside, focus the first control on open, restore focus to the opener on close, and close on Escape. Previously focus could leak to the page behind the modal. 4. Card structural fix (WCAG 4.1.2 / 1.3.1) - The card was role="button" containing the menu button, checkbox, and rename input — an invalid a11y tree patched with stopPropagation. Now the row is a plain <li> in a <ul role="list">; the open/resume affordance is a real <button> wrapping the title; menu/checkbox/rename are siblings. - Date-group + Pinned labels are now <h3> inside <section aria-labelledby> (WCAG 1.3.1 / 2.4.6) so the list is navigable by heading. New shared hook: screens/shared/useFocusTrap.ts. i18n (en/es/zh-CN): newGroupTitle, newGroupPlaceholder, newGroupCreate. typecheck clean; Sessions tests 19/19 (added 5: focus-into-menu, arrow roving, Escape-returns-focus, real Open button, new-group modal not window.prompt); full suite +5 passing, 24 pre-existing infra failures unchanged; build exit 0. Co-Authored-By: Claude <noreply@anthropic.com>
fix(sessions): Tier-3 a11y structural — focus-managed menu, modal traps, card semantics
…L engine) Adds an LLM Council feature driven by PAL MCP: the running Opus 4.8 agent orchestrates a panel of (free) models, one per named position, then synthesizes as chairman. No remote service, no token in the renderer — the desktop authors a convene instruction the agent fulfils via PAL, reusing the existing chat pipeline. Composer (ChatInput): new Council button submits the convene prompt through onSubmit; the context gauge moves to the far right of the toolbar. Models screen: new "LLM Council" tab (CouncilTab) with - add/remove panel members + chairman selection - positions CRUD with editable, self-learning descriptions (thumbs up/down → agent-proposed refinement the user accepts/rejects) - model advisor: ranks the pool for a task with accuracy/speed shown as learning tiers (never fabricated %) and free/paid badges Storage: per-profile council-config.json (never config.yaml, which is gated). Types shared via src/shared/council.ts; 13 council-* IPC handlers; single en council i18n namespace (other locales fall back per-key). Verified: typecheck clean (web+node), 96/96 unit tests pass, electron-vite build + electron-builder --dir pack succeed (council code confirmed inside the packed asar), CDP screenshot confirms the tab + composer render without clipping.
Greptile SummaryThis PR adds Anthropic Claude OAuth (native PKCE paste-a-code flow), a multi-profile session aggregator with desktop-owned pin/status/group metadata, a Factory governance tab, and an LLM Council configuration panel.
Confidence Score: 3/5Safe to merge for most users, but the Anthropic OAuth token persistence passes live credentials through the process argv, which is readable by other same-user processes on the machine. The multi-profile session aggregator and Factory/Council UI additions are well-structured. The Anthropic PKCE flow is the concerning path: access_token and refresh_token are handed to Python as positional sys.argv arguments, making them briefly visible in the OS process table to any process running under the same UID. The success detection also uses a substring check that could silently report success on a failed token-persistence run. These issues live on a high-value path (OAuth credential storage) and warrant a fix before the feature ships widely. src/main/hermes-auth.ts — the persistAnthropicToken function and its subprocess invocation deserve the most attention.
|
| Filename | Overview |
|---|---|
| src/main/hermes-auth.ts | Adds Anthropic PKCE OAuth flow natively in the main process; tokens are passed as CLI argv to the Python persistence script (process table exposure), and the success check uses a substring match that could misfire on error messages. |
| src/main/sessions.ts | Adds multi-profile session aggregation with desktop-owned metadata tables (pin, status, groups); the openProfileDb readonly parameter is a dead ternary but intentionally always opens writable. Logic is otherwise sound. |
| src/main/index.ts | Wires up ~100 new IPC handlers for Anthropic OAuth, Council config, multi-profile sessions, and Factory governance; the pendingAnthropic state is scoped correctly and the Anthropic flow lacks an activeProc-style guard but this is not dangerous given Node's event-loop serialisation. |
| src/renderer/src/components/OAuthLoginModal.tsx | Extended to handle the Anthropic paste-a-code PKCE path alongside the existing CLI-loopback flow; StrictMode double-invoke guard is in place and the success/cancel paths are correct. |
| src/main/kanban.ts | Adds four Factory governance functions (governStatus, governSet, governKillSwitch, governModels) that delegate to hermes kanban govern; clean and consistent with existing kanban helpers. |
| src/main/council-config.ts | New per-profile JSON-backed Council config store; well-structured with safe defaults and no cross-profile leakage. |
| src/renderer/src/screens/Factory/Factory.tsx | New Factory governance UI with auto-refresh, toast notifications, and activity filters; no logic issues observed. |
Sequence Diagram
%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant UI as OAuthLoginModal (Renderer)
participant IPC as Electron IPC (Main)
participant Auth as hermes-auth.ts
participant Anthropic as Anthropic OAuth
participant Python as Python subprocess
UI->>IPC: anthropic-oauth-start
IPC->>Auth: buildAnthropicAuthUrl()
Auth-->>IPC: "{ url, verifier, state }"
IPC-->>UI: "{ url }"
UI->>Anthropic: open browser → user authorizes
Anthropic-->>UI: "user pastes code#state"
UI->>IPC: anthropic-oauth-submit(code, profile)
IPC->>Auth: exchangeAnthropicCode(code, verifier, state)
Auth->>Anthropic: POST /v1/oauth/token (PKCE)
Anthropic-->>Auth: "{ access_token, refresh_token, expires_in }"
Auth->>Python: execFile(python, ["-c", script, access_token, refresh_token, expires_in])
Note over Python: tokens visible in process table
Python-->>Auth: stdout OK label
Auth-->>IPC: "{ success: true, persisted: true }"
IPC-->>UI: success → onSuccess()
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
participant UI as OAuthLoginModal (Renderer)
participant IPC as Electron IPC (Main)
participant Auth as hermes-auth.ts
participant Anthropic as Anthropic OAuth
participant Python as Python subprocess
UI->>IPC: anthropic-oauth-start
IPC->>Auth: buildAnthropicAuthUrl()
Auth-->>IPC: "{ url, verifier, state }"
IPC-->>UI: "{ url }"
UI->>Anthropic: open browser → user authorizes
Anthropic-->>UI: "user pastes code#state"
UI->>IPC: anthropic-oauth-submit(code, profile)
IPC->>Auth: exchangeAnthropicCode(code, verifier, state)
Auth->>Anthropic: POST /v1/oauth/token (PKCE)
Anthropic-->>Auth: "{ access_token, refresh_token, expires_in }"
Auth->>Python: execFile(python, ["-c", script, access_token, refresh_token, expires_in])
Note over Python: tokens visible in process table
Python-->>Auth: stdout OK label
Auth-->>IPC: "{ success: true, persisted: true }"
IPC-->>UI: success → onSuccess()
Reviews (1): Last reviewed commit: "feat(council): LLM Council — composer co..." | Re-trigger Greptile
…-tools feat(agent-detail): per-agent persona editor + skills/tools (WIP) — persona tested
There was a problem hiding this comment.
Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.
…indows path mock (#11) Two Windows-only failures (13 tests), both pre-existing on main, both real bugs: 1. CommandSecretsProvider hardcoded execFileSync("/bin/sh") — Node resolves that against the Win32 filesystem where it doesn't exist (the MSYS/Git-Bash /bin/sh is a shell-mount illusion), so every secret spawn returned ENOENT and degraded to null. Added resolveShell(): bare "sh" on POSIX (PATH-resolved), and on win32 prefer an existing Git-for-Windows sh.exe / $SHELL, else bare "sh" for WSL/Cygwin. The injection-safety + stderr-non-leak invariants now actually execute on Windows (they were silently dark). +5 resolveShell regression tests. 2. config-health.test.ts mocked installer HERMES_HOME as a hardcoded POSIX "/tmp/..." while the on-disk fixture uses join(tmpdir(),...). On Linux these coincide; on Windows join() yields backslashed %TEMP% paths, so the REAL existsSync(configFile) returned false and EMPTY_API_SERVER_KEY was gated off. Derive the mock HERMES_HOME from tmpdir() so mock and fixture stay in lockstep cross-platform. Full suite: 1243 passed / 0 failed / 3 skipped (was 13 failed on Windows). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
There was a problem hiding this comment.
Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.
… WIP typecheck fixes (#12) * test(agent-detail): cover per-agent persona/skills/tools panel; fix WIP typechecks - AgentDetail.test.tsx (5 tests): default persona tab, profile threading to Soul/Skills/Tools, initialTab deep-link (Office clickthrough), onClose, backdrop-vs-modal click. The per-agent config panel had zero coverage. - sessions.ts: SearchResult requires lastActivity:number but the search mapping omitted it (TS2322). Map it to started_at (rows already recency- ordered; lastActivity is display/sort only). - i18n/index.ts: annotate sharedI18n: i18n to fix TS2742 (inferred type not portable without an i18next node_modules reference). Both tsc projects (node + web) clean. Full suite 1260 passed / 3 skipped; only the known gateway-restart parallel-timing flake remains (17/17 isolated). * feat(agent-detail): add per-agent persona/skills/tools panel component Composes the profile-aware Soul (persona), Skills and Tools screens into one tabbed overlay, so a single agent's persona, assigned skills and enabled toolsets all live in one place — reachable per-agent. Each child already takes a profile prop and reads/writes that profile's own files (SOUL.md, skills/, config toolsets), so this is pure composition, no new backend. Pairs with AgentDetail.test.tsx (committed prior) — that test imported this file; committing it here so the import resolves and CI builds. The Agents-list and Office-3D clickthrough wiring that render this panel are separate WIP edits (Agents.tsx / Office.tsx) not included here.
There was a problem hiding this comment.
Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.
…gents (#13) Click an agent in the Office tab to open the AgentDetail panel (persona/ skills/tools) for that profile — the settings are now reachable directly from the Office, not only the Agents grid. Wires Office.tsx -> AgentDetail (from #12), adds the Agents Settings affordance + onBrowseSkills passthrough in Layout. Verified: tsc --noEmit clean; Office/Agents/Layout suites 15/15 green. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
There was a problem hiding this comment.
Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.
…) — DO NOT MERGE (#14) Verbatim salvage of an uncommitted working-tree WIP that grew SidebarRecentSessions 219 -> 847 lines: per-session context menu (rename/delete/pin/archive) + keyboard a11y (roving tabindex, portal popover), plus its 10KB test file. Committed UNREVIEWED to make it durable after an earlier git-checkout clobber nearly lost it. NOT verified, NOT reviewed, NOT for merge. Provenance uncertain (likely a human dev's half-finished branch). Triage notes follow in the PR/handoff. Bridge-pill + CSP allowlist edits deliberately kept on a SEPARATE branch. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
There was a problem hiding this comment.
Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.
… MERGE (#15) Verbatim salvage of uncommitted WIP: a live Bridge status pill in Settings (up-to-date/syncing/error/off) and the CSP connect-src allowlist entry it needs (http://127.0.0.1:8770 in src/main/index.ts + src/renderer/index.html). SECURITY NOTE: the CSP change widens connect-src — must get its own focused review before merge (flagged by the council's security seat). Committed UNREVIEWED only to make it durable. NOT verified, NOT for merge. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
There was a problem hiding this comment.
Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.
#16) Resolves the divergence behind PR #9 (35 ahead / 55 behind). Union-merged both sides across 13 conflicted files; typecheck (node+web) and full test suite (1438 passed) green. Kept from this fork (ours): - Council (config + advisor + en/council locale) - Factory tab/panel/toggle + orchestrator closed-loop + factory CSS - Multi-profile sessions API (listAllSessions / syncAllSessionCaches / searchAllSessions / *InProfile / *ByProfile / session groups) - AIR-008 config-health precedence tests + Windows cross-platform fix - Version 0.6.5 Adopted from upstream (v0.6.2): - Complete Hebrew (he) locale with RTL support - Web Preview panel + chat toolbar toggle - Follow-us / X-follow chat strings - applySessionLocalOverlays session overlays - onConnectionConfigChanged reconciliation (reset+reload on connection change) - Session-load race guards (loadRequestId) + preserve-on-empty refresh - DB connection caching (getDbConnection) Semantic-merge fixes: - remote-sessions.ts: add lastActivity to SearchResult builders (ours made the field required); update fixtures accordingly - sessions.ts: drop now-unused activeStateDbPath import - Sessions.tsx: cast listCachedSessions fallback to SessionRow[] - Sessions.test.tsx: multi-profile mock + onConnectionConfigChanged; adapt upstream race-guard tests to the agg() helper Signed-off-by: Avi-Bendetsky <146357256+Avi-Bendetsky@users.noreply.github.com>
|
Too many files changed for review. ( |
* fix(secrets): make command-provider timeout overridable to de-flake spawn tests The secrets command provider used a hardcoded 3s timeout. Under a CPU-saturated parallel test run, spawning a real `sh` helper can exceed 3s just to start, tripping the timeout and resolving a spurious null (flaky, unrelated to the code). - resolveCommandTimeoutMs() reads HERMES_SECRET_COMMAND_TIMEOUT_MS at CALL time (tests only); production never sets it and keeps the tight 3s UI-freeze ceiling. Parsed defensively: missing/blank/non-numeric -> 3s. - Tests set a generous budget so spawn latency can't cause false nulls. - vitest testTimeout/hookTimeout raised to 20s (runner deadline, not the spawn cap). - New tests/command-timeout-override.test.ts covers the resolver. Verified: 5 passed / 3 skipped on the affected suites. * fix(test): de-flake full-suite secrets/gateway spawn tests via pool cap Root cause (layered, verified): 1. ~27 test files spawn REAL child processes (sh helpers in the secrets provider, gateway lifecycle, CLI fallbacks). Vitest's default forks pool runs one worker PER CPU core (8 here) and each worker spawns its own children — oversubscribing the cores many times over. A test that takes ~2s isolated was observed taking 30s+ under that contention (16x), tripping deadlines and producing flaky failures unrelated to the code under test. 2. The secrets command-provider spawn budget wasn't overridable everywhere, so the provider's tight 3s production timeout could itself trip under load. Fix: - vitest.config: cap pool to maxForks=4 (half the cores) so spawned children get CPU headroom; add a 20s testTimeout/hookTimeout safety ceiling. - commandProvider: resolveCommandTimeoutMs() reads HERMES_SECRET_COMMAND_TIMEOUT_MS at call time (TESTS ONLY); production keeps the 3s default. - api-server-key + commandProvider tests set a generous spawn budget. - Drop a broken/redundant throwaway (command-timeout-override.test.ts) whose internal plumbing is already exercised by the real spawn tests. Verified: 3 consecutive full-suite runs 0 failures; 135 test files pass; tsc clean both projects. (Was: 1-3 flaky failures per full run.) 🤖 Generated with [Claude Code](https://claude.com/claude-code)
…+ worker-pool cap) (#18) * fix(secrets): make command-provider timeout overridable to de-flake spawn tests The secrets command provider used a hardcoded 3s timeout. Under a CPU-saturated parallel test run, spawning a real `sh` helper can exceed 3s just to start, tripping the timeout and resolving a spurious null (flaky, unrelated to the code). - resolveCommandTimeoutMs() reads HERMES_SECRET_COMMAND_TIMEOUT_MS at CALL time (tests only); production never sets it and keeps the tight 3s UI-freeze ceiling. Parsed defensively: missing/blank/non-numeric -> 3s. - Tests set a generous budget so spawn latency can't cause false nulls. - vitest testTimeout/hookTimeout raised to 20s (runner deadline, not the spawn cap). - New tests/command-timeout-override.test.ts covers the resolver. Verified: 5 passed / 3 skipped on the affected suites. * fix(test): de-flake full-suite secrets/gateway spawn tests via pool cap Root cause (layered, verified): 1. ~27 test files spawn REAL child processes (sh helpers in the secrets provider, gateway lifecycle, CLI fallbacks). Vitest's default forks pool runs one worker PER CPU core (8 here) and each worker spawns its own children — oversubscribing the cores many times over. A test that takes ~2s isolated was observed taking 30s+ under that contention (16x), tripping deadlines and producing flaky failures unrelated to the code under test. 2. The secrets command-provider spawn budget wasn't overridable everywhere, so the provider's tight 3s production timeout could itself trip under load. Fix: - vitest.config: cap pool to maxForks=4 (half the cores) so spawned children get CPU headroom; add a 20s testTimeout/hookTimeout safety ceiling. - commandProvider: resolveCommandTimeoutMs() reads HERMES_SECRET_COMMAND_TIMEOUT_MS at call time (TESTS ONLY); production keeps the 3s default. - api-server-key + commandProvider tests set a generous spawn budget. - Drop a broken/redundant throwaway (command-timeout-override.test.ts) whose internal plumbing is already exercised by the real spawn tests. Verified: 3 consecutive full-suite runs 0 failures; 135 test files pass; tsc clean both projects. (Was: 1-3 flaky failures per full run.) 🤖 Generated with [Claude Code](https://claude.com/claude-code) * release(0.6.6): cross-platform release docs + version bump - bump 0.6.5 -> 0.6.6 for GitHub Release + auto-update - release/RELEASE-NOTES-0.6.6.md, BUILD-MAC.md, release.json - bundles: LLM Council, sidebar session menu, CC Bridge pill, CSP localhost fix - bridge is per-user (no Anthropic session ships); signing roadmap documented * docs(release): add 0.6.6 release notes, Mac build guide, manifest
No description provided.