diff --git a/packages/coding-agent/docs/providers.md b/packages/coding-agent/docs/providers.md index 0f64ceb5db..0f45624073 100644 --- a/packages/coding-agent/docs/providers.md +++ b/packages/coding-agent/docs/providers.md @@ -188,7 +188,9 @@ curl https://cursor.com/install -fsS | bash **Multi-account behavior.** Each account gets its own durable credential home under `/cursor-cli-oauth/accounts//home` (directory mode 0700, `.cursor/auth.json` mode 0600, file credential store), which also holds that account's CLI chat history and is never deleted between turns. One senpi session sticks to one account (rendezvous hashing), and a rate-limited or auth-failing account is blocked with a cooldown while the turn fails over to the next account before any visible output; once output has started, the error surfaces instead of replaying. -**Model switching on resume.** Each turn resumes the same CLI chat id (`--resume`). Switching the model mid-session keeps that chat, and the first post-switch turn carries a short recap block built from senpi's own recent exchanges so the new model re-orients (`contextRecapOnModelSwitch`, default on). A CLI-side context overflow restarts a fresh chat with the same recap instead of wedging the session. +**Model switching on resume.** Each turn resumes the same CLI chat id (`--resume`). Switching the model mid-session keeps that chat, and the first post-switch turn carries a short recap block built from senpi's own recent exchanges so the new model re-orients (`contextRecapOnModelSwitch` or `SENPI_CURSOR_CLI_OAUTH_RECAP`, default on). A CLI-side context overflow restarts a fresh chat with the same recap instead of wedging the session. + +**Provider switching.** Entering `cursor-cli-oauth` after another provider also prepends the bounded recent-exchange recap so Cursor receives the intervening Senpi context. This behavior is controlled independently by `contextRecapOnProviderSwitch` (or `SENPI_CURSOR_CLI_OAUTH_PROVIDER_RECAP`, default on). Setting `resumeMode: "off"` remains context-free between consecutive Cursor turns. **No-approval acknowledgement.** This lane is the one case where a senpi provider runs tools senpi cannot gate: with force execution, the Cursor CLI executes its own tools autonomously - there is no senpi approval, no senpi sandboxing, and no tool-level audit for what it runs. The first force execution therefore refuses with the exact acknowledgement step: `/cursor-account acknowledge` (or set `cursorCliOauthProvider.noApprovalAcknowledgedAt` to the current ISO-8601 timestamp in senpi settings, once). With force execution disabled instead, the lane still answers but the Cursor CLI auto-rejects every tool call (one warning per session). diff --git a/packages/coding-agent/docs/settings.md b/packages/coding-agent/docs/settings.md index e211f00dbd..019a42427d 100644 --- a/packages/coding-agent/docs/settings.md +++ b/packages/coding-agent/docs/settings.md @@ -325,6 +325,8 @@ When unset, senpi leaves provider payloads unchanged. This setting currently app |---------|------|---------|-------------| | `claudeSdkOauthProvider.enabled` | boolean | `false` | Enable the ambient (host-CLI-derived) lane of `claude-sdk-oauth`. Env override: `SENPI_CLAUDE_SDK_OAUTH_ENABLED`. Explicit senpi-side logins (stored OAuth accounts in `auth.json`, `CLAUDE_CODE_OAUTH_TOKEN` / `CLAUDE_CODE_OAUTH_TOKEN_` env accounts) keep the provider available with this unset | | `cursorCliOauthProvider.enabled` | boolean | `false` | Enable the `cursor-cli-oauth` fallback lane and automatic native credential bootstrap. Env override: `SENPI_CURSOR_CLI_OAUTH_ENABLED` | +| `cursorCliOauthProvider.contextRecapOnModelSwitch` | boolean | `true` | Prepend a bounded recent-context recap when a resumed Cursor chat changes models. Env override: `SENPI_CURSOR_CLI_OAUTH_RECAP` | +| `cursorCliOauthProvider.contextRecapOnProviderSwitch` | boolean | `true` | Prepend a bounded recent-context recap when entering `cursor-cli-oauth` after another provider. Env override: `SENPI_CURSOR_CLI_OAUTH_PROVIDER_RECAP` | Both ambient-auth providers are explicit opt-in: a vendor CLI being logged in on the machine is not consent to spend that subscription. Before these gates existed, a logged-in Claude Code or `cursor-agent` CLI made the lane available with no senpi-side action, so subscription usage could flow through a provider you never configured. Env overrides follow the usual precedence (`env > project settings > global settings > default`). See [providers.md](providers.md) for the full lane documentation. diff --git a/packages/coding-agent/src/core/extensions/builtin/cursor-cli-oauth/AGENTS.md b/packages/coding-agent/src/core/extensions/builtin/cursor-cli-oauth/AGENTS.md index 8df7e795f4..8c8c15479f 100644 --- a/packages/coding-agent/src/core/extensions/builtin/cursor-cli-oauth/AGENTS.md +++ b/packages/coding-agent/src/core/extensions/builtin/cursor-cli-oauth/AGENTS.md @@ -9,7 +9,7 @@ Generated: 2026-08-17 | File | Role | |---|---| | `index.ts` | Extension entry: registers the `cursor-cli-oauth` provider unconditionally with the offline model catalog (swapping in the probe-backed catalog when it resolves), wires `/cursor-account`, installs the default native-bootstrap credential reader, and delegates every turn to `streamSimple` with settings, accounts, and the executable re-resolved per turn | -| `settings.ts` | `cursorCliOauthProvider` settings parsing: fixed defaults (`enabled: false`, `forceExecution: true`, `executionMode: "agent"`, `resumeMode: "auto"`, `contextRecapOnModelSwitch: true`, `modelCatalogTtlHours: 24`), `SENPI_CURSOR_CLI_OAUTH_*` env overrides winning over disk, invalid values silently ignored; exports the sandbox-allowlist validator factory | +| `settings.ts` | `cursorCliOauthProvider` settings parsing: fixed defaults (`enabled: false`, `forceExecution: true`, `executionMode: "agent"`, `resumeMode: "auto"`, `contextRecapOnModelSwitch: true`, `contextRecapOnProviderSwitch: true`, `modelCatalogTtlHours: 24`), `SENPI_CURSOR_CLI_OAUTH_*` env overrides winning over disk, invalid values silently ignored; exports the sandbox-allowlist validator factory | | `accounts.ts` | Multi-account slot model: `CursorCliAccountSlot` and the credential shape with sentinel top-level fields, `addAccount`/`removeAccount`/`pinAccount`/`listAccounts`, name validation `^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$`, `assertSentinelInvariant`, `refreshSlot` through `store.modify` | | `native-bootstrap.ts` | Default credential reader: when enabled, the executable resolves, and managed accounts are empty, copies the native Senpi `cursor` OAuth credential into one canonical `native` slot; re-checks the target under the auth lock, deduplicates concurrent reads, never overwrites incompatible/existing credentials, and degrades non-throwingly | | `affinity.ts` | HRW (rendezvous) account selection keyed by senpi session id (sha256 `BigUInt64BE` score), pinned account wins unless blocked, expired `rate_limit` blocks cleared while `auth_error` blocks persist, `AllCursorAccountsBlockedError` carrying the soonest unblock time | @@ -20,7 +20,7 @@ Generated: 2026-08-17 | `transport.ts` | Spawns the resolved executable detached in its own process group with an explicit env allowlist (`HOME` = the account home, `AGENT_CLI_CREDENTIAL_STORE=file`, `PATH`/`TERM`/`LANG`/`LC_ALL`/`FORCE_COLOR`); rejects prompts over 130 KB pre-spawn; abort sends SIGTERM to the group then SIGKILL after 5 s; exposes the pid, parsed events, bounded stderr, and a settled outcome | | `home-store.ts` | Durable per-account HOMEs under `/cursor-cli-oauth/accounts//home`: rewrites `.cursor/auth.json` (`accessToken`/`refreshToken`/`apiKey: null`/`bedrockCredentials: null`) at mode 0600 inside 0700 directories immediately before each run, reads back rotated refresh tokens after; logs byte lengths only; traversal-checked paths; never deletes a HOME | | `oauth-login.ts` | Provider OAuth config (`check`/`login`/`refreshToken`/`getApiKey`) reusing the `packages/ai` Cursor PKCE flow, first slot named `default`; one `configuredFor` predicate backs both `check` and turn-time lane resolution (`file-store` only, no ambient branch exists); local desktop/keychain import remains explicit, while `importNativeCursorCredential` is shared by explicit and automatic Senpi-native credential copies | -| `session-router.ts` | Sticky chat routing: per-senpi-session `{accountName, chatId, lastModel}` captured from `system/init`; same- or different-model turns resume via `--resume`, a model switch prepends a one-turn 8 KB context recap, and resume failure or `context_overflow` restarts a fresh chat with the recap plus a notice; prompt and recap are shrunk to the transport ceiling before spawning | +| `session-router.ts` | Sticky chat routing: per-senpi-session `{accountName, chatId, lastModel}` captured from `system/init`; same- or different-model turns resume via `--resume`, transitions from another provider and later Cursor model switches independently opt into a one-turn 8 KB context recap, and resume failure or `context_overflow` restarts a fresh chat with the recap plus a notice; prompt and recap are shrunk to the transport ceiling before spawning | | `failover.ts` | Account rotation around one attempt: `rate_limit` blocks the slot (server hint else 60 s, max 48 h), `auth_error` blocks until re-login; retries only before any visible assistant delta; a replacement account always starts a fresh chat with a user-visible notice and never inherits chat context | | `models.ts` | Model catalog: cached `cursor-agent models` probe (15 s deadline, full-stdout file capture, ANSI strip, ` -