diff --git a/AGENTS.md b/AGENTS.md index b7870f82..2b1fd3fd 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -83,6 +83,7 @@ App docs: - `/app/L0/_all/mod/_core/agent/AGENTS.md` - `/app/L0/_all/mod/_core/agent-chat/AGENTS.md` - `/app/L0/_all/mod/_core/agent_prompt/AGENTS.md` +- `/app/L0/_all/mod/_core/anthropic_oauth/AGENTS.md` - `/app/L0/_all/mod/_core/dashboard/AGENTS.md` - `/app/L0/_all/mod/_core/dashboard_welcome/AGENTS.md` - `/app/L0/_all/mod/_core/documentation/AGENTS.md` @@ -197,7 +198,7 @@ Project concepts: - when `CUSTOMWARE_GIT_HISTORY` is enabled, writable `L1//` and `L2//` roots are treated as optional per-owner local Git repositories with adaptive-debounced server-side commits and rollback APIs - `USER_FOLDER_SIZE_LIMIT_BYTES` optionally caps each `L2//` folder on disk; app-file mutations are checked against a cached per-user size total and only size-reducing mutations are allowed once a user folder is already over the limit - runtime file discovery is backed by sharded `file_index` state; startup indexes `L0`, `L1`, and layer roots, auth first touch loads only the target user's auth files, and full `L2/` shards are loaded only on demand for file/module access or active mutations rather than preloaded just because their folders exist on disk -- runtime parameters are defined in `commands/params.yaml`; `node space serve` resolves them in this order: launch arguments, stored `.env` params written by `node space set`, then process environment variables, then schema defaults; `node space supervise` accepts the same runtime parameters, owns the public `HOST` and `PORT`, requires `CUSTOMWARE_PATH`, enables source auto-update by default, and passes the remaining resolved params to private `space serve` children; `WORKERS` controls clustered HTTP worker count for `serve` and `supervise`; `CUSTOMWARE_PATH` is the parent directory for writable backend `L1/` and `L2/` storage when configured and also hosts backend-owned cloud-share archives under `share/spaces/` when that feature is enabled; `CUSTOMWARE_WATCHDOG` defaults to `true` and controls live backend customware watching, config watching, and the periodic reconcile backstop without disabling L0/L1 startup indexing, on-demand L2 loading, or explicit clustered mutation sync; `GIT_BACKEND` defaults to `auto` and may force `native` or `isomorphic` for server-owned Git flows such as local history and Git-backed module operations; `LOGIN_ALLOWED` gates the password-login endpoints and login-shell form, `CLOUD_SHARE_ALLOWED` gates hosted cloud-share uploads, `CLOUD_SHARE_URL` tells browser clients which hosted share receiver to use, and page shells receive only `frontend_exposed` values as injected meta tags +- runtime parameters are defined in `commands/params.yaml`; `node space serve` resolves them in this order: launch arguments, stored `.env` params written by `node space set`, then process environment variables, then schema defaults; `node space supervise` accepts the same runtime parameters, owns the public `HOST` and `PORT`, requires `CUSTOMWARE_PATH`, enables source auto-update by default, and passes the remaining resolved params to private `space serve` children; `WORKERS` controls clustered HTTP worker count for `serve` and `supervise`; `CUSTOMWARE_PATH` is the parent directory for writable backend `L1/` and `L2/` storage when configured and also hosts backend-owned cloud-share archives under `share/spaces/` when that feature is enabled; `CUSTOMWARE_WATCHDOG` defaults to `true` and controls live backend customware watching, config watching, and the periodic reconcile backstop without disabling L0/L1 startup indexing, on-demand L2 loading, or explicit clustered mutation sync; `GIT_BACKEND` defaults to `auto` and may force `native` or `isomorphic` for server-owned Git flows such as local history and Git-backed module operations; `LOGIN_ALLOWED` gates the password-login endpoints and login-shell form, `CLOUD_SHARE_ALLOWED` gates hosted cloud-share uploads, `CLOUD_SHARE_URL` tells browser clients which hosted share receiver to use, `ANTHROPIC_OAUTH_ALLOWED` gates the Claude subscription OAuth provider for the agent surfaces and `ANTHROPIC_OAUTH_CLIENT_ID`, `ANTHROPIC_OAUTH_AUTHORIZE_URL`, `ANTHROPIC_OAUTH_TOKEN_URL`, `ANTHROPIC_OAUTH_REDIRECT_URI`, `ANTHROPIC_OAUTH_FLOW_MODE`, and `ANTHROPIC_API_BASE_URL` configure that flow with sane public defaults that work without a per-deployment Anthropic OAuth registration; `ANTHROPIC_OAUTH_FLOW_MODE` defaults to `auto` and selects a button-only redirect flow on `localhost` hosts and the manual code-paste flow elsewhere, and page shells receive only `frontend_exposed` values as injected meta tags - app file APIs use logical app-rooted paths such as `L2/alice/user.yaml` or `/app/L2/alice/user.yaml`, and supported endpoints may also accept `~` or `~/...` for the authenticated user's `L2//...`; those logical paths do not change when `CUSTOMWARE_PATH` relocates the writable backend roots - non-`/api` and non-`/mod` browser entry routes are served from `server/pages/`; `/login` and `/enter` are public and the protected page shells live behind the router-side session gate - detailed browser-runtime rules live in `/app/AGENTS.md` diff --git a/app/AGENTS.md b/app/AGENTS.md index 2930cdfe..8e9ef92f 100644 --- a/app/AGENTS.md +++ b/app/AGENTS.md @@ -53,6 +53,7 @@ Current module-local docs in the app tree: - `app/L0/_all/mod/_core/onscreen_agent/AGENTS.md` - `app/L0/_all/mod/_core/onscreen_menu/AGENTS.md` - `app/L0/_all/mod/_core/open_router/AGENTS.md` +- `app/L0/_all/mod/_core/anthropic_oauth/AGENTS.md` - `app/L1/_all/mod/metrics/posthog/AGENTS.md` - `app/L0/_admin/mod/_core/overlay_agent/AGENTS.md` @@ -133,6 +134,7 @@ Current major first-party modules under `app/L0/_all/mod/_core/`: - `onscreen_agent/`: floating routed overlay agent and the first-party user-facing chat runtime - `onscreen_menu/`: reserved routed shell header bar, Home shortcut to the empty default route, left and right shell-control seams, and `_core/onscreen_menu/items` dropdown action seam - `open_router/`: headless OpenRouter request-policy module that extends the admin and onscreen API transport seams instead of hardcoding provider-specific headers into the chat runtimes +- `anthropic_oauth/`: optional Claude subscription LLM provider; small headless helper plus a reusable connect block embedded under a third tab in the admin and onscreen settings dialogs, plus the per-surface request hooks that redirect API-mode requests to the authenticated `/api/anthropic_subscription_completions` endpoint when the user opted into subscription mode - `web_browsing/`: browser-surface module that contributes a Browser dropdown action, mounts draggable, minimizable, resizable popup browser windows, and defines placement-generic `` elements that can also live inside widgets or other screen DOM; the same element uses an iframe fallback in browser sessions and a DOM-backed desktop `` in the packaged host, supports optional `controls="true"` address-bar chrome, registers every surface under a unique `browser-N` id exposed through the numeric-id `space.browser` runtime, includes in-app interception of `_blank` or `window.open(...)` requests back into new modals, persists only popup-window geometry across reloads, tracks direct surface focus for prompt-time browser content, and gates app-side browser diagnostics through a shared browser log level that defaults to `error` - `skillset/`: first-party shared skill packs plus browser helper scripts and shared browser-side skill discovery helpers used by the onscreen and admin agents - `webllm/`: unlisted routed browser-only WebLLM test surface with a module-local worker, vendored browser runtime, compact searchable prebuilt model loading, expert-only compiled custom model loading, and simple throughput reporting diff --git a/app/L0/_all/mod/_core/admin/views/agent/AGENTS.md b/app/L0/_all/mod/_core/admin/views/agent/AGENTS.md index 705608d2..77dda2d1 100644 --- a/app/L0/_all/mod/_core/admin/views/agent/AGENTS.md +++ b/app/L0/_all/mod/_core/admin/views/agent/AGENTS.md @@ -29,7 +29,7 @@ Current persistence paths: Current stored config fields are written in YAML as: -- `llm_provider` +- `llm_provider` (currently `api`, `subscription`, or `local`) - `local_provider` - `api_endpoint` - `api_key` @@ -76,7 +76,9 @@ Prompt rules: Current behavior: -- the LLM settings modal keeps one provider switch at the top with tabs named `API` and `Local`, and shows either the API settings fields or one `Local` section +- the LLM settings modal keeps one provider switch at the top with three tabs named `API key`, `Claude subscription`, and `Local`, and shows the API settings fields, the subscription connect block, or the `Local` section based on the active tab +- the `Claude subscription` tab is owned by `_core/anthropic_oauth/`; it mounts `connect-block.html` through `` and shows a Claude model name input alongside it +- when the active provider is `subscription`, `api.js` validation skips the `apiEndpoint` and `apiKey` checks and the `_core/anthropic_oauth/` request hook redirects the prepared fetch URL to the authenticated `/api/anthropic_subscription_completions` endpoint while stripping any `Authorization` header so the browser never sees the OAuth bearer token - the `Local` section only supports the Hugging Face browser runtime - the toolbar LLM settings button summarizes the current selection with the configured model name only; it does not prepend provider labels such as `API`, `Local`, or `Hugging Face` - the local section mounts the standalone Hugging Face config sidebar component through ``, so the admin modal and the routed testing harness share the same component file instead of maintaining duplicated local-provider markup diff --git a/app/L0/_all/mod/_core/admin/views/agent/api.js b/app/L0/_all/mod/_core/admin/views/agent/api.js index e36a2f31..ef2ad1d9 100644 --- a/app/L0/_all/mod/_core/admin/views/agent/api.js +++ b/app/L0/_all/mod/_core/admin/views/agent/api.js @@ -457,12 +457,17 @@ export const prepareAdminAgentApiRequest = globalThis.space.extend( ); async function streamAdminAgentApiCompletion({ promptContext, settings, systemPrompt, messages, onDelta, signal }) { - if (!settings.apiEndpoint.trim()) { - throw new Error("Set an API endpoint before sending a message."); - } + const provider = config.normalizeAdminChatLlmProvider(settings?.provider); + const isSubscription = provider === config.ADMIN_CHAT_LLM_PROVIDER.SUBSCRIPTION; - if (!settings.apiKey.trim()) { - throw new Error("Set an API key before sending a message."); + if (!isSubscription) { + if (!settings.apiEndpoint.trim()) { + throw new Error("Set an API endpoint before sending a message."); + } + + if (!settings.apiKey.trim()) { + throw new Error("Set an API key before sending a message."); + } } if (!settings.model.trim()) { diff --git a/app/L0/_all/mod/_core/admin/views/agent/config.js b/app/L0/_all/mod/_core/admin/views/agent/config.js index 2a00ef4a..e413e89c 100644 --- a/app/L0/_all/mod/_core/admin/views/agent/config.js +++ b/app/L0/_all/mod/_core/admin/views/agent/config.js @@ -5,7 +5,8 @@ export const ADMIN_CHAT_HISTORY_PATH = "~/hist/admin-chat.json"; export const DEFAULT_ADMIN_CHAT_MAX_TOKENS = 120_000; export const ADMIN_CHAT_LLM_PROVIDER = { API: "api", - LOCAL: "local" + LOCAL: "local", + SUBSCRIPTION: "subscription" }; export const ADMIN_CHAT_LOCAL_PROVIDER = { @@ -26,9 +27,13 @@ export const DEFAULT_ADMIN_CHAT_SETTINGS = { }; export function normalizeAdminChatLlmProvider(value) { - return value === ADMIN_CHAT_LLM_PROVIDER.LOCAL - ? ADMIN_CHAT_LLM_PROVIDER.LOCAL - : ADMIN_CHAT_LLM_PROVIDER.API; + if (value === ADMIN_CHAT_LLM_PROVIDER.LOCAL) { + return ADMIN_CHAT_LLM_PROVIDER.LOCAL; + } + if (value === ADMIN_CHAT_LLM_PROVIDER.SUBSCRIPTION) { + return ADMIN_CHAT_LLM_PROVIDER.SUBSCRIPTION; + } + return ADMIN_CHAT_LLM_PROVIDER.API; } export function normalizeAdminChatLocalProvider(value) { diff --git a/app/L0/_all/mod/_core/admin/views/agent/panel.html b/app/L0/_all/mod/_core/admin/views/agent/panel.html index bf67faaa..ac7a26ed 100644 --- a/app/L0/_all/mod/_core/admin/views/agent/panel.html +++ b/app/L0/_all/mod/_core/admin/views/agent/panel.html @@ -3,7 +3,9 @@ + +
@@ -186,7 +188,15 @@

Provider and model configuration

:class="{ 'is-active': $store.adminAgent.isSettingsDraftUsingApiProvider }" @click="$store.adminAgent.setSettingsProvider('api')" > - API + API key + +
+
+ + +
@@ -284,7 +311,7 @@

Provider and model configuration

System, history, and transient rebalance to 100% of max tokens. Single history message is a percentage of the history budget.

-