Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion .github/checks-manifest.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -361,7 +361,7 @@ checks:
reason: "public-build contract and GitHub variable scope fixtures must remain executable"
- id: web-app-checks
command: ["bash", "web/app/test.sh"]
triggers: ["web/app/src/**", "web/app/package.json", "web/app/bun.lock", "web/app/tsconfig.json", "web/app/vitest.config.mts", "web/app/vitest.setup.ts", "web/app/scripts/moonshine-smoke.test.ts", "web/app/test.sh", ".github/checks-manifest.yaml"]
triggers: ["web/app/src/**", "web/app/package.json", "web/app/bun.lock", "web/app/tsconfig.json", "web/app/vitest.config.mts", "web/app/vitest.setup.ts", "web/app/tailwind.config.ts", "web/app/scripts/moonshine-smoke.test.ts", "web/app/scripts/check-feature-imports.ts", "web/app/scripts/check-feature-imports.test.ts", "web/app/test.sh", "web/app/ARCHITECTURE.md", "web/app/AGENTS.md", ".github/checks-manifest.yaml"]
lanes: ["local", "ci"]
reason: "web/app shipped with no typecheck or test lane at all, so desktop-parity work there had no runnable Definition-of-Done gate"
- id: admin-deploy-scope
Expand Down
1 change: 0 additions & 1 deletion backend/tests/unit/test_byok_security.py
Original file line number Diff line number Diff line change
Expand Up @@ -843,7 +843,6 @@ def test_requires_validated_context(self, monkeypatch):
monkeypatch.setattr(subscription, 'get_byok_keys', lambda: {'openai': 'sk'})
assert subscription.request_has_llm_byok_key() is False


def test_quota_snapshot_accepts_required_llm_provider(self, monkeypatch):
from models.users import PlanType
from utils import subscription
Expand Down
5 changes: 1 addition & 4 deletions backend/utils/subscription.py
Original file line number Diff line number Diff line change
Expand Up @@ -221,10 +221,7 @@ def request_has_llm_byok_key() -> bool:
try:
fingerprints = get_cached_byok_state(uid).get('fingerprints', {})
except Exception:
return any(
get_byok_key(provider)
for provider in ('openrouter', 'openai', 'anthropic', 'gemini')
)
return any(get_byok_key(provider) for provider in ('openrouter', 'openai', 'anthropic', 'gemini'))
return any(
provider in fingerprints and bool(get_byok_key(provider))
for provider in ('openrouter', 'openai', 'anthropic', 'gemini')
Expand Down
4 changes: 2 additions & 2 deletions docs/agents/web-app-destinations.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ page, moving a surface between pages, or debugging an animation that looks stuck

| Route | Holds |
|---|---|
| `/home` | The hub **and** the chat. Chat is not a separate page: `web/app/src/components/home/HomePage.tsx` keeps recent history above the hub and the current exchange below it, with transcript rendering owned by `web/app/src/components/chat/ChatTranscript.tsx`. Live capture also starts here, from the composer. |
| `/home` | The hub **and** the chat. Chat is not a separate page: `web/app/src/features/home/ui/HomePage.tsx` keeps recent history above the hub and the current exchange below it, with transcript rendering owned by `web/app/src/features/chat/ui/ChatTranscript.tsx`. Live capture also starts here, from the composer. |
| `/conversations` | Conversations and daily recaps in one day-grouped gallery. A recap is the summary of a day, so it leads that day rather than living in a list of its own. |
| `/memories`, `/tasks` | As named. |
| `/connectors` | Installed apps **and** external services — the former Settings → Integrations. |
Expand All @@ -30,7 +30,7 @@ because they share this component, not because someone kept them in sync.
`PageHeader` is the different thing it looks like: a back button beside a title,
for detail and sub pages.

shadcn/ui is set up (`web/app/components.json`, primitives in `web/app/src/components/ui/`
shadcn/ui is set up (`web/app/components.json`, primitives in `web/app/src/shared/ui/`
lowercase). Its generator emits literal `oklch(...)` strings, which are not
valid classes in this project, so every primitive is restyled onto the Omi
tokens on the way in. Do that for any primitive you add.
Expand Down
2 changes: 1 addition & 1 deletion docs/agents/web-app-signals.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ so the next person does not reintroduce the pattern it replaced.
| Need | Use |
|---|---|
| Read something from the API | `useAsyncResource(key, fetcher)` (`web/app/src/hooks/useAsyncResource.ts`) |
| A list that is also written optimistically | A signal store — see `createGoalsStore` in `web/app/src/hooks/useGoals.ts` |
| A list that is also written optimistically | A signal store — see `createGoalsStore` in `web/app/src/features/goals/useGoals.ts`. Same pattern: conversations, recaps, memories, chat, search, notifications, knowledge graph, Gemini Live, goal advice |
| Subscribe a component to a raw signal | `useSignalValue` / `useResourceValue` (`web/app/src/lib/signals.ts`) |

`useAsyncResource` is keyed: change the key and it refetches, pass `null` and it
Expand Down
4 changes: 2 additions & 2 deletions docs/product/invariants/memory-tiers.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ default access policy of Short-term + Long-term.
- `desktop/macos/Desktop/Sources/MemoryBankConnector.swift`
- `desktop/macos/Desktop/Sources/Rewind/Core/MemoryStorage.swift`
- `desktop/macos/Desktop/Sources/Rewind/Core/MemoryModels.swift`
- `web/app/src/components/memories/**`
- `web/app/src/lib/memoryExport.ts`
- `web/app/src/features/memories/**`
- `web/app/src/features/memories/memoryExport.ts`
- `web/frontend/src/components/memories/**`

## PR rule
Expand Down
11 changes: 10 additions & 1 deletion web/app/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,15 @@
The signed-in Omi web client: React 19 on the `@tschk/moonshine` runtime, served
by Bun. Siblings: `web/admin`, `web/frontend`, `web/personas-open-source`.

Code layout: domain modules under `src/features/<name>/{api,model,ui}` plus
`src/shared` (HTTP kernel and UI primitives). Map and import rules:
[`ARCHITECTURE.md`](./ARCHITECTURE.md). Guard: `bun run check:imports`.
Outside a feature import only its `index.ts`, `api.ts`, or `model.ts`.

Current domains: `auth`, `chat`, `connectors`, `conversations` (includes recaps),
`fair-use`, `goals`, `home`, `marketplace`, `memories` (**INV-MEM-1**),
`notifications`, `recording`, `settings`, `tasks`.

## Setup

```bash
Expand Down Expand Up @@ -44,7 +53,7 @@ reads, a signal store for optimistically-written lists. Import only the kernel
why and the pitfalls: [`docs/agents/web-app-signals.md`](../../docs/agents/web-app-signals.md).

Tests live in `__tests__/` beside the code (`vitest.config.mts`, jsdom, `@` →
`src/`). Prefer pure logic in `src/lib/` and `src/hooks/`; reserve component
`src/`). Prefer pure logic in `src/features/<domain>/model.ts`; reserve component
rendering for behavior that only appears in the tree.

## Parity With Desktop
Expand Down
39 changes: 39 additions & 0 deletions web/app/ARCHITECTURE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# web/app architecture

Signed-in Omi web client: React 19 on `@tschk/moonshine`. Destinations live in
`src/app`; product code lives in `src/features/<domain>` and shared kernel in
`src/shared`.

## Layers (inside a feature)

| Path | Holds | Must not |
|---|---|---|
| `model.ts` (and other pure helpers) | grouping, ids, parsing | React, `fetch`, `./api` |
| `api.ts` | HTTP for this domain | UI |
| `ui/` | components | ad-hoc `fetch`; thick helpers |
| hooks (`useX.ts`) | current React state / orchestration | a second data-layer |

Cross-feature and `src/app` imports use the feature's public `index.ts` only.
`src/shared` does not import `src/features`. Enforced by
`scripts/check-feature-imports.ts` (part of `bun run check`).

## Kernel vs features

- `src/shared/api/client.ts` — `fetchWithAuth` and authorized blob/audio headers
- `src/shared/ui` — Toast, dialogs, input, maps, startup modals (no feature imports)
- `src/lib` — firebase, cache, utils, generated OpenAPI types (not yet moved)
- `src/components/layout` — shell chrome (may import features)
- Tailwind `content` is `src/**` so feature class names reach `styles.css`
- `src/hooks/useAsyncResource.ts`, `useLocalStorage`, `useRequestOwner`,
`useScrollEdges` — generic hooks, not a domain

Writable moonshine stores hold lists and session state written from the UI
(goals, tasks, people, conversations, recaps, memories, chat, search,
notifications, knowledge graph, Gemini Live, goal advice). Read-only fetches
use `useAsyncResource` (`useHomeTasks`, `useGoalDetail` history).

## Features (grow as domains move)

See `src/features/*/ARCHITECTURE.md` once a folder exceeds twelve source files.
The destination map is unchanged: `/home` is hub+chat, `/conversations` includes
recaps, `/connectors` is apps+services.
Loading
Loading