Skip to content

feat(web): per-Agent login, session identity & team management — web layer (HT-54) - #71

Merged
zaridan merged 13 commits into
mainfrom
feat/ht-54-agents-auth-web
Jul 19, 2026
Merged

feat(web): per-Agent login, session identity & team management — web layer (HT-54)#71
zaridan merged 13 commits into
mainfrom
feat/ht-54-agents-auth-web

Conversation

@zaridan

@zaridan zaridan commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

The web half of specs/auth/agents-and-auth.md (HT-54), on top of #70's engine. Deploys coordinated with the engine (spec §10): both breaking changes (assignee shape, session v2) land in one synchronized deploy; nothing auto-deploys.

What's here

  • Session v2 — the cookie payload carries sub (the Agent id); mintSessionCookie(sub) requires it at the type level, and the middleware's sliding refresh threads the verified sub (the spec's called-out silent identity-drop trap). v1 cookies fail closed → one re-login.
  • API clientrequest() reads+verifies the session cookie and attaches X-Helpthread-Agent-Id exactly per the spec's per-endpoint rule; a 401 on a header-carrying call routes to a new SESSION digest (log in again), bearer 401s keep the AuthFailure path.
  • Auth actions — login/setup/invite-accept delegate to the engine's password provider; the web-side scrypt comparison and HELPTHREAD_UI_PASSWORD are retired.
  • Screens (six)/setup, per-Agent /login (driven by GET /auth/providers), /settings/team (list+search), /settings/team/new (invite-primary + admin-set password), /settings/team/[id] (profile: role, disable, password, timezone, two-step delete, resend invite), /invite/[token]. Avatar menu wired to the real Agent.
  • Assignee, real Agents — roster-driven menu, "Assign to me" via /auth/me, Mine/Unassigned/Assigned computed on assigneeAgentId.

⚠️ maintainer fidelity sign-off REQUIRED (ticket not Done without it)

Five net-new designed surfaces beyond the frozen prototype, each flagged in its module doc: SetupScreen, TeamListScreen, NewAgentScreen, AgentProfileScreen, InviteAcceptScreen (LoginScreen extended). All composed from ds/** primitives — git diff -- web/src/components/ds/ is empty. Known sign-off-eye items: role chip vs Disabled pill render similarly on the team list; ds TextInput limits the login email field's autocomplete semantics (HT-52 note extended).

Review & verification

  • Implemented (Sonnet) from the spec + as-built engine; two independent adversarial reviews (correctness/session-security; charter/fidelity/vocab/a11y) — verdicts FIX-THEN-SHIP ×2; all actionable findings fixed (invited-Agent Save dead-end, a11y role groups, setup error copy, invite helper text) plus one live-found bug: the timezone select lacked UTC (not in Intl.supportedValuesOf) and would have silently rewritten UTC Agents' timezones.
  • Driven end-to-end in a real browser against the dev engine: setup→auto-login; team list; create (admin-set password path); logout; wrong-password generic 401; login as non-admin; roster-driven assignee (assign to another Agent, folder counts update); non-admin team-route redirect; invite accept over a real hti. token (set password → signed in) and replay rejected with the calm invite-invalid copy; invited-Agent edit Save persists post-fix; timezone shows UTC.
  • Gates (sequential, real exit codes): typecheck ✅ lint ✅ full suite ✅ 1022 testsnext build ✅ (all routes dynamic).
  • No web test runner exists (root vitest covers src/** only) — session/middleware behavior is covered by the engine-side suite + the browser drive; noted, not silently skipped.

Charter: no new dependencies; no ds/** edits; Agents/Team vocabulary throughout; FreeScout used as black-box UX reference only.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added first-run /setup, per-agent email/password login, and public invite acceptance via /invite/{token} (with invalid/expired handling).
    • Introduced admin “Team/Agents” area under /manage/agents, including agent profile management and mailbox permissions.
    • Updated conversations/inbox to drive “Mine/Assigned/Unassigned” and assignee labeling from the agent roster.
    • Improved session handling with a “session expired” full-screen sign-out flow and refreshed top-level UI personalization.
    • Added keyboard shortcuts access from Settings.
  • Documentation
    • Refreshed setup/auth/routing and “Where things live” docs for the per-agent account model.

zaridan and others added 8 commits July 18, 2026 15:04
Session payload becomes {v:2, iat, sub} — sub is the signed-in Agent's
id, threaded from mintSessionCookie's now-required first parameter so
the compiler rejects any identity-less re-mint. middleware.ts's
sliding-refresh re-stamp threads session.payload.sub through instead
of silently dropping it. PUBLIC_PATHS gains /setup; a new
PUBLIC_PREFIXES rule covers /invite/{token}. uiAuthConfig() drops
HELPTHREAD_UI_PASSWORD entirely — the engine is the verification
authority now (specs/auth/agents-and-auth.md §8).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Types (Agent, SelfAgent, AuthProviderDescriptor) and typed functions
(getAuthProviders, postSetup, postVerify, getMe, listAgents,
createAgent, getAgent, patchAgent, deleteAgent, setAgentPassword,
resendInvite, acceptInvite) typed 1:1 against the engine branch's
src/api/agents.ts handlers, including the {agent}/{agents}/
{providers,needsSetup}/inviteSent wrapper shapes.

request() gains an internal actingAgent option: verifies the session
cookie via next/headers and attaches X-Helpthread-Agent-Id, throwing
a session-expired ApiError (no engine call) when there is no valid
session to assert. Per spec §8's pinned rule, the header rides on
every /agents/* call, /auth/me, and putAssignee — never on the
pre-session bootstrap endpoints or the other existing conversation
calls. A 401 that carried the header now gets a NEW SESSION_ERROR_DIGEST
(auth-error.ts) instead of AUTH_ERROR_DIGEST, so the two failure modes
("your session is stale" vs "the deployment's service token is bad")
route to different recoveries.

putAssignee's body becomes {assigneeAgentId} (was {assignee: 'me'|null})
— the breaking change specs/auth/agents-and-auth.md §10 calls out.
ConversationSummary.assigneeAgentId replaces the old assignee field.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…T-54)

loginAction/setupAction/acceptInviteAction call postVerify/postSetup/
acceptInvite and mint the session cookie from the returned Agent's id
— the engine is the sole verification authority now (scrypt hash-at-rest,
constant-time compare, no account enumeration, spec §4/§9). The
scryptSync/timingSafeEqual plaintext-env compare from HT-51 is deleted
along with the ~500ms failure delay: that delay blunted a scripted
guesser against a web-side plaintext compare that no longer exists: a
web-side sleep on top of the engine's own timing posture adds nothing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Four new/extended public and shell surfaces (specs/auth/agents-and-auth.md
§7), each carrying LoginScreen's existing "NEW designed surface — requires
TJ fidelity sign-off" flag and reusing its two documented ds/ workarounds
(native password input, Button.requestSubmit()) rather than inventing new
ones:

- /setup (SetupScreen, public): name/email/password+confirm, zero-Agents-
  guarded — the page redirects to /login once an Agent already exists.
- /login (LoginScreen, extended): renders one email+password form per
  kind:'credentials' provider from GET /auth/providers; redirects to /setup
  when needsSetup.
- /invite/{token} (InviteAcceptScreen, public): set password + confirm;
  the engine's 401 (invalid/expired token) renders inline as a calm error
  — there's no separate token-validation endpoint to pre-check against.
- AppError.tsx gains a SessionExpired branch for SESSION_ERROR_DIGEST: signs
  out via the existing logoutAction (clears the cookie, redirects to
  /login) instead of showing AuthFailure's deployment-token copy, which
  would be the wrong story for a stale per-Agent session.

TopBar's avatar menu now shows the signed-in Agent's name/email/initials
(root layout.tsx fetches getMe(), swallowing a 401 the same way it already
swallows the notifications fetch — this layout has no error boundary above
it) and "Your profile" routes to /settings/team/{selfId}.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
/settings/team (admin-only UI; roster cards with initials, role chip,
invited/disabled badges, client-side search), /settings/team/new (role,
first/last name, email, invite-or-admin-password provisioning — "Send
an invite email" defaults on with "An invite can be sent later," an
admin-set password field appears only when it's off), and
/settings/team/{id} (name/timezone editable by self-or-admin, role and
Disabled toggle admin-only — Disabled hidden on self so an admin can't
lock themselves out here, change/reset password, resend invite while
invited, and Delete for admin-on-another using ConversationScreen's
two-step arm pattern, never confirm()).

A non-admin visiting /settings/team or /settings/team/new — or someone
else's /settings/team/{id} — is redirected to their own profile instead
of ever reaching the engine's 403: the simpler of the brief's two
options, and it keeps an engine 403 from ever rendering as a crash.

agent-actions.ts is the write path: unlike actions.ts's conversation
mutations (bearer-only, so each action re-verifies the session cookie
itself), every call here goes through api.ts's actingAgent option,
which already verifies the session and refuses to call the engine
without one — no redundant hasValidSession() gate needed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
ConversationScreen's assignee control now lists the Agent roster
(listAgents(), ACTIVE Agents only) instead of the old single-operator
Anyone/Me toggle: "Anyone" (null), "Assign to me" (resolves selfId from
getMe()), then every other active Agent by name. putAssigneeAction's
signature follows the api.ts body-shape change (assigneeAgentId).

Unassigned/Mine/Assigned (InboxScreen, folder-counts.ts) now split on
assigneeAgentId against the viewing Agent's own id, not the retired
'me' sentinel — every route that renders these folders (the shell
layout's rail counts, /dashboard, /inbox/[folder], /conversations/[id])
now fetches getMe() to supply that id. Next's fetch request memoization
collapses the repeated getMe() calls within one render pass to a single
network round trip.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The env-var docs now describe the /setup-first-run + per-Agent-login
story (specs/auth/agents-and-auth.md §8) instead of HT-51's single
shared operator password, and the "Shipped so far" section lists the
Agents & Authentication surfaces this branch adds.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Two-lens adversarial review + live browser drive of the full stack:
- invited-Agent Save dead-end (MAJOR): an admin Save always bundled
  `status`, which the engine's closed lifecycle 409s on invited Agents —
  every edit of a pending invite failed; status is now omitted for invited
- timezone select gains 'UTC': Intl.supportedValuesOf('timeZone') does not
  include it (verified live), so the engine-default 'UTC' fell back to the
  first option and a Save silently rewrote it to Africa/Abidjan
- role segmented controls: broken label htmlFor (no matching control)
  replaced with semantic <fieldset aria-label> per biome a11y
- setupAction: non-409 failures now show generic copy, never raw internals
- invite-accept: 8-char helper text (consistency with /setup)
- LoginScreen doc: the email field's TextInput limitation added to the
  HT-52 tracking note

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 18, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The web application shifts from shared operator authentication to per-Agent accounts, signed Agent sessions, setup and invite flows, Agent administration, mailbox permissions, Agent-based assignment, and identity-aware inbox navigation.

Changes

Per-Agent authentication and onboarding

Layer / File(s) Summary
Session and acting-Agent contracts
web/src/lib/session.ts, web/src/lib/api.ts, web/src/lib/api-types.ts, web/src/middleware.ts
Session cookies now carry Agent IDs, API requests attach acting-Agent identity, and authentication and mailbox endpoints use typed engine-backed APIs.
Setup, login, and invite flows
web/src/app/login/page.tsx, web/src/app/setup/*, web/src/app/invite/*, web/src/components/{LoginScreen,SetupScreen,InviteAcceptScreen}.tsx, web/src/lib/auth-actions.ts
Setup, credential login, and invite acceptance validate forms, call engine-backed actions, mint Agent sessions, and redirect after success.

Team management

Layer / File(s) Summary
Team roster and Agent management
web/src/app/manage/agents/*, web/src/components/{TeamListScreen,NewAgentScreen,AgentProfileScreen,AgentPermissionsScreen,AgentDetailShell}.tsx, web/src/lib/agent-actions.ts
Admin routes and screens now list, create, update, disable, delete, invite, password-manage, and assign mailbox permissions for Agents.

Agent-aware inbox

Layer / File(s) Summary
Inbox and conversation assignment
web/src/app/(shell)/{inbox,conversations}/*, web/src/components/{InboxScreen,ConversationScreen}.tsx, web/src/lib/{folder-counts,actions}.ts
Inbox filters, folder counts, conversation assignee state, and assignment mutations now use assigneeAgentId and the viewing Agent ID.

Authenticated shell

Layer / File(s) Summary
Shell, navigation, recovery, and settings UI
web/src/app/{layout.tsx,dashboard/page.tsx,(shell)/layout.tsx}, web/src/components/{TopBar,AppError,SettingsScreen,ShortcutsProvider}.tsx, web/src/lib/{auth-error,format}.ts, web/README.md
Authenticated shell data and navigation use the current Agent, expired sessions trigger logout handling, shortcut access moves to Settings, and documentation reflects the per-Agent model.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 45.56% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the main web-layer changes around per-Agent auth, session identity, and team management.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/ht-54-agents-auth-web

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@web/src/components/AgentProfileScreen.tsx`:
- Around line 93-103: Reset all local profile state whenever the Agent changes,
including name, timezone, role, disabled status, password fields, validation
errors, and delete-arming state, keyed by agent.id in the AgentProfileScreen
component; alternatively key the component by agent.id from the route page so it
remounts for each agent. Ensure navigation between agents cannot retain the
previous agent’s values when patchAgentAction submits updates.

In `@web/src/components/ConversationScreen.tsx`:
- Around line 1022-1029: Handle rejected server-action promises consistently: in
web/src/components/ConversationScreen.tsx lines 1022-1029, update updateAssignee
to catch request rejection and restore the previous assignee; in
web/src/components/NewAgentScreen.tsx lines 114-139, catch the relevant action
rejection and populate the form error; in
web/src/components/AgentProfileScreen.tsx lines 109-180, add rejection handling
to the save, password, invite, and deletion flows while preserving each flow’s
existing error behavior.
- Around line 673-675: Update the conversation-ID reset effect in
ConversationScreen to also reset assigneeAgentId from the newly selected
conversation’s assigneeAgentId, alongside the existing state resets. Ensure
navigating between conversations cannot retain or mutate the previous
conversation’s assignee.

In `@web/src/components/InviteAcceptScreen.tsx`:
- Around line 93-106: Update the error branch in InviteAcceptScreen so
recoverable acceptInviteAction failures, including “Could not reach the server,”
keep the invite form and retry controls visible while displaying the error above
them. Only hide the form when the action provides an explicit terminal-error
discriminator for an expired or otherwise invalid invite.
- Around line 43-57: Add overflowY: 'auto' to the fixed container style in
InviteAcceptScreen’s returned root div, matching SetupScreen’s behavior so
invite controls remain accessible and the form scrolls on short viewports.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: a7249b8a-9633-4e76-af50-3c69f3d361b8

📥 Commits

Reviewing files that changed from the base of the PR and between 9243582 and f684377.

📒 Files selected for processing (32)
  • web/README.md
  • web/src/app/(shell)/conversations/[id]/page.tsx
  • web/src/app/(shell)/inbox/[folder]/page.tsx
  • web/src/app/(shell)/layout.tsx
  • web/src/app/dashboard/page.tsx
  • web/src/app/invite/[token]/page.tsx
  • web/src/app/layout.tsx
  • web/src/app/login/page.tsx
  • web/src/app/settings/team/[id]/page.tsx
  • web/src/app/settings/team/new/page.tsx
  • web/src/app/settings/team/page.tsx
  • web/src/app/setup/page.tsx
  • web/src/components/AgentProfileScreen.tsx
  • web/src/components/AppError.tsx
  • web/src/components/ConversationScreen.tsx
  • web/src/components/InboxScreen.tsx
  • web/src/components/InviteAcceptScreen.tsx
  • web/src/components/LoginScreen.tsx
  • web/src/components/NewAgentScreen.tsx
  • web/src/components/SetupScreen.tsx
  • web/src/components/TeamListScreen.tsx
  • web/src/components/TopBar.tsx
  • web/src/lib/actions.ts
  • web/src/lib/agent-actions.ts
  • web/src/lib/api-types.ts
  • web/src/lib/api.ts
  • web/src/lib/auth-actions.ts
  • web/src/lib/auth-error.ts
  • web/src/lib/folder-counts.ts
  • web/src/lib/format.ts
  • web/src/lib/session.ts
  • web/src/middleware.ts

Comment thread web/src/components/AgentProfileScreen.tsx
Comment thread web/src/components/ConversationScreen.tsx
Comment thread web/src/components/ConversationScreen.tsx Outdated
Comment thread web/src/components/InviteAcceptScreen.tsx
Comment thread web/src/components/InviteAcceptScreen.tsx Outdated
- AgentProfileScreen keyed by agent.id (state re-seeds on A→B navigation)
- ConversationScreen's per-conversation reset re-seeds assigneeAgentId
  (deps stay conversation.id-only — re-seeding on same-conversation field
  refresh would wipe an open composer; documented ignore)
- rejected server-action invocations normalized everywhere (assignee
  rollback; NewAgent form error; profile flows via an invokeAction helper;
  invite accept)
- invite-accept: container scrolls on short viewports; transient failures
  keep the form (retry) while a dead token shows the terminal panel

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Manage ▾ is the global-admin doorway (Settings, Team) — admins only,
  whole trigger; avatar menu is personal-only (Your Profile, Log out)
- routes: /settings/team* → /manage/agents* ; per-Agent area gains a
  left sidebar (Profile / Permissions) via AgentDetailShell
- new Permissions screen: All/None quick links + per-mailbox checkboxes
  (admin targets get the implicit-access note), wired to the as-built
  engine mailbox-access endpoints
- keyboard shortcuts moved under Settings; ? key unchanged

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@zaridan

zaridan commented Jul 19, 2026

Copy link
Copy Markdown
Contributor Author

Amended per the maintainer's admin-IA fidelity review (77c7dd2): Manage ▾ is now the global-admin doorway (Settings, Team; admins only, whole trigger); the avatar menu is personal-only (Your Profile, Log out); routes moved /settings/team*/manage/agents* with the per-Agent Profile/Permissions sidebar; the new Permissions screen wires to PR #72's mailbox-access endpoints (All/None + per-mailbox checkboxes; admin targets get the implicit-access note); keyboard shortcuts moved under Settings. The IA contract itself is specs/ui/admin-ia.md in #72. Browser-verified end-to-end against #72's dev API (grant roundtrip persisted, auto-grant pre-checked, 403/400 matrices). Merge order: #72 first, then rebase-check this PR. Screens remain pending the maintainer's fidelity sign-off.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
web/src/components/TeamListScreen.tsx (1)

79-80: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Use Agent for human support staff.

  • web/src/components/TeamListScreen.tsx#L79-L80: change “Invite your first teammate” to “Invite your first Agent.”
  • web/README.md#L32-L37: replace “single shared operator password” with wording that does not label staff as operators.

As per coding guidelines, use Agents exclusively for human support staff.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/src/components/TeamListScreen.tsx` around lines 79 - 80, Update the
empty-state text in TeamListScreen.tsx to say “Invite your first Agent.” Also
revise the wording in web/README.md at lines 32-37 to replace “single shared
operator password” with terminology that does not call human support staff
operators, using “Agent” or “Agents” consistently.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@web/src/components/AgentPermissionsScreen.tsx`:
- Around line 61-70: Update save() to catch promise rejections from
putAgentMailboxesAction, ensuring invocation failures display the same failure
toast instead of escaping the transition. Follow the existing error-handling
pattern used by NewAgentScreen while preserving the current result-based
!result.ok handling and success toast.

---

Outside diff comments:
In `@web/src/components/TeamListScreen.tsx`:
- Around line 79-80: Update the empty-state text in TeamListScreen.tsx to say
“Invite your first Agent.” Also revise the wording in web/README.md at lines
32-37 to replace “single shared operator password” with terminology that does
not call human support staff operators, using “Agent” or “Agents” consistently.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 8e24e371-aedd-4e5f-89fa-99db7047802d

📥 Commits

Reviewing files that changed from the base of the PR and between bb81212 and 77c7dd2.

📒 Files selected for processing (16)
  • web/README.md
  • web/src/app/manage/agents/[id]/page.tsx
  • web/src/app/manage/agents/[id]/permissions/page.tsx
  • web/src/app/manage/agents/new/page.tsx
  • web/src/app/manage/agents/page.tsx
  • web/src/components/AgentDetailShell.tsx
  • web/src/components/AgentPermissionsScreen.tsx
  • web/src/components/AgentProfileScreen.tsx
  • web/src/components/NewAgentScreen.tsx
  • web/src/components/SettingsScreen.tsx
  • web/src/components/ShortcutsProvider.tsx
  • web/src/components/TeamListScreen.tsx
  • web/src/components/TopBar.tsx
  • web/src/lib/agent-actions.ts
  • web/src/lib/api-types.ts
  • web/src/lib/api.ts

Comment thread web/src/components/AgentPermissionsScreen.tsx
zaridan and others added 3 commits July 18, 2026 17:57
…creen (HT-54)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
General / Appearance / Keyboard shortcuts down a left rail (the same
FolderItem rail the Agent detail area uses) — Settings sections are the
injection points HT-56 and modules extend; client-side section state
within the one route until sections gain depth.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A theme is a personal preference — the three-scope rule puts it in the
personal scope, not Manage → Settings (TJ). Device-local persistence for
now; account-synced preference rides HT-61.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@zaridan
zaridan merged commit 502bc6b into main Jul 19, 2026
5 checks passed
@zaridan
zaridan deleted the feat/ht-54-agents-auth-web branch July 19, 2026 01:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant