Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
b7ce936
docs(proposals): structured output works on a Claude plan
AnishSarkar22 Oct 16, 2026
abafc09
feat(local): find the user's Claude Code and read its sign-in
AnishSarkar22 Oct 16, 2026
e95f47d
feat(local): build every Claude Code run's options and read its failures
AnishSarkar22 Oct 16, 2026
e1c9c04
feat(local): talk to Claude Code through one run adapter
AnishSarkar22 Oct 16, 2026
af5f15c
feat(local): let a connection be a Claude plan
AnishSarkar22 Oct 16, 2026
eb6e94f
feat(local): connect a Claude plan from the user's own Claude Code
AnishSarkar22 Oct 16, 2026
b4d92da
feat(local): answer chat through the user's own Claude Code
AnishSarkar22 Oct 16, 2026
edeb6b3
feat(local): answer the app's chat on a Claude plan
AnishSarkar22 Oct 16, 2026
26fc208
feat(local): send Claude Code's traffic through a proxy the API runs
AnishSarkar22 Oct 16, 2026
37f91f1
refactor(local): read nvm versions and proxy tunnels without regex
AnishSarkar22 Oct 16, 2026
25c689d
test(local): keep the Agent SDK's own Claude Code out of the app
AnishSarkar22 Oct 16, 2026
45cf8c5
build(local): install the Agent SDK without its own Claude Code
AnishSarkar22 Oct 16, 2026
9640a2a
feat(local): name a Claude plan's failures in Claude's terms
AnishSarkar22 Oct 16, 2026
aba52ea
feat(local): set up a Claude plan from Settings and onboarding
AnishSarkar22 Oct 16, 2026
5af836e
docs(local): a ChatGPT sign-in asks about its hosts in one question
AnishSarkar22 Oct 16, 2026
daf9fb3
refactor(local): record egress consent for several hosts in one request
AnishSarkar22 Oct 16, 2026
280194f
docs(adr): one consent question per user action
AnishSarkar22 Oct 16, 2026
c4763ae
feat(local): keep every Claude Code process inside egress, and watch …
AnishSarkar22 Oct 16, 2026
e95b8d3
feat(local): say so when a host is turned off during a reply
AnishSarkar22 Oct 16, 2026
6c7ce81
docs(local): ask how the Claude agent reaches a plugin
AnishSarkar22 Oct 16, 2026
111e0aa
docs(local): settle the Claude agent's plugins, skills and sessions f…
AnishSarkar22 Oct 16, 2026
d2d23ac
refactor(local): reach the agent through an engine interface
AnishSarkar22 Oct 16, 2026
3348059
docs(local): the Claude agent does everything opencode's does
AnishSarkar22 Oct 16, 2026
38b9c15
feat(local): agent threads record their engine, and keep a Claude Cod…
AnishSarkar22 Oct 16, 2026
74d47a1
feat(local): opencode's permission rules for Claude Code's tool calls
AnishSarkar22 Oct 16, 2026
c912d3e
feat(local): read a Claude Code turn as opencode's, for one frame and…
AnishSarkar22 Oct 16, 2026
e66a08d
feat(local): what an agent run's Claude Code is launched with
AnishSarkar22 Oct 16, 2026
7c701f3
feat(local): the Claude plan dialog says its models run Agentic mode too
AnishSarkar22 Oct 16, 2026
904fac3
test(local): check the agent engine's assumptions against the install…
AnishSarkar22 Oct 16, 2026
cfc2a3a
feat(local): Agentic threads run on Claude Code for a Claude plan model
AnishSarkar22 Oct 16, 2026
5c41b6d
fix(local): the Claude plan dialog is wider than the egress question …
AnishSarkar22 Oct 16, 2026
ae8265a
fix(local): a shorter Claude plan egress question, so it fits inside …
AnishSarkar22 Oct 16, 2026
b4cb1f8
fix(local): the Claude sign-in host says what it does, not what it lacks
AnishSarkar22 Oct 16, 2026
9d2ea9c
fix(local): the Claude plan row shows at once, and the dialog says to…
AnishSarkar22 Oct 16, 2026
b810d57
fix(local): tunnels through SurfSense's proxy work under uvloop
AnishSarkar22 Oct 16, 2026
86b94ae
fix(local): a deleted connection is never asked about again, and a pl…
AnishSarkar22 Oct 16, 2026
8ce2b6c
fix(local): new tools show as a badge beside On, not in place of it
AnishSarkar22 Oct 16, 2026
1c05eb8
fix(local): the uvloop proxy test skips where uvloop is not installed
AnishSarkar22 Oct 16, 2026
fd171c8
Merge remote-tracking branch 'upstream/dev' into feat/claude-plan
AnishSarkar22 Oct 16, 2026
8baa186
fix(local): a Claude plan turn names a plugin unavailable only once C…
AnishSarkar22 Oct 16, 2026
5cbbb06
fix(local): a Claude plan turn reads a large tool result Claude Code …
AnishSarkar22 Oct 16, 2026
4b4f135
fix(local): onboarding's on-device model list shows about three and a…
AnishSarkar22 Oct 16, 2026
897846c
feat(local): onboarding shows its progress as a filling bar instead o…
AnishSarkar22 Oct 16, 2026
2b840ae
Merge remote-tracking branch 'upstream/dev' into feat/claude-plan
AnishSarkar22 Oct 16, 2026
6bdbb96
docs(local): the Claude plan is architecture now, not a proposal
AnishSarkar22 Oct 16, 2026
3c3e73b
fix(local): close three Claude plan gaps and delete every engine's se…
AnishSarkar22 Oct 17, 2026
83f7b0d
fix(local): close more Claude plan gaps and say above the composer wh…
AnishSarkar22 Oct 17, 2026
7f030d5
fix(local): a Claude plan reply on extra usage goes on instead of sto…
AnishSarkar22 Oct 17, 2026
af5f45e
fix(local): refuse a Claude Code too old for the plan before it runs,…
AnishSarkar22 Oct 17, 2026
635a427
test(local): the Claude plan tests find their stand-in Claude Code on…
AnishSarkar22 Oct 17, 2026
e2c1d2a
docs(local): a thread's engine moves with the selected model; claude_…
AnishSarkar22 Oct 17, 2026
8178ed7
fix(local): a thread opens while the engine it left cannot answer
AnishSarkar22 Oct 17, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions .agents/skills/translate/glossary.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,10 @@ SurfSense, Studio, Markdown, llama.cpp, GGUF, Hugging Face, API, MCP, and every
| session log | セッションログ | 세션 로그 | 会话日志 | सेशन लॉग | Sitzungsprotokoll | registro de la sesión | journal de session | log da sessão | журнал сеанса |
| clipboard | クリップボード | 클립보드 | 剪贴板 | क्लिपबोर्ड | Zwischenablage | portapapeles | presse-papiers | área de transferência | буфер обмена |
| embedding (the model that turns the library into vectors) | 埋め込み | 임베딩 | 嵌入 | एम्बेडिंग | Embedding | embedding | embedding | embedding | эмбеддинг |
| sign in (to a ChatGPT account) | サインイン | 로그인 | 登录 | साइन इन | anmelden | iniciar sesión | se connecter | entrar | войти |
| plan (a ChatGPT plan, Plus or Pro) | プラン | 플랜 | 套餐 | प्लान | Plan | plan | forfait | plano | план |
| sign in (to a ChatGPT account, or Claude Code to a Claude plan) | サインイン | 로그인 | 登录 | साइन इन | anmelden | iniciar sesión | se connecter | entrar | войти |
| plan (a ChatGPT plan, Plus or Pro; a Claude plan, Pro, Max or Team) | プラン | 플랜 | 套餐 | प्लान | Plan | plan | forfait | plano | план |
| subscription (using a ChatGPT plan instead of a key) | サブスクリプション | 구독 | 订阅 | सब्सक्रिप्शन | Abo | suscripción | abonnement | assinatura | подписка |
| terminal (where the user runs `claude`, the command that stays in English) | ターミナル | 터미널 | 终端 | टर्मिनल | Terminal | terminal (una) | terminal | terminal | терминал |

## What the English means

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# ADR 0057: A Claude plan is reached through the user's own Claude Code, and SurfSense holds no Claude credential

- **Status:** Accepted
- **Status:** Accepted; its network decision superseded by [ADR 0061](0061-claude-code-reaches-anthropic-and-in-the-agent-the-turns-plugins.md)
- **Date:** 2026-10-16
- **Supersedes:** [ADR 0038](0038-chatgpt-plans-sign-in-through-openai-not-codex.md) in part: its consequence that a Claude subscription is not offered. [ADR 0050](0050-a-model-answers-on-its-own-route-with-its-connections-credential.md) in part: that SurfSense owns a plan's sign-in for both engines, which now holds for the ChatGPT plan only
- **Source:** [Use your Claude plan proposal, decisions and Anthropic's terms L59–84](https://github.com/MODSetter/SurfSense/blob/e1332966dd2ec2cfcf636331da6f770d836fcff1/docs/proposals/claude-plan/README.md#L59-L84)
Expand Down Expand Up @@ -33,4 +33,4 @@ A Claude plan has no HTTP endpoint SurfSense may call, so opencode cannot reach

## Where the code stands

Not built. The work is laid out in the [Use your Claude plan](../proposals/claude-plan/README.md) proposal.
Built: chat, titles, Studio and Agentic mode ([Claude plan](../architecture/claude-plan.md)). It is untested on Windows and Linux on a real install. The contract tests against the real binary that the consequences name run only on a developer's machine, opted in; a Claude Code newer than the one they last passed on is named in the log, and the version SurfSense calls tested is raised only once they pass again ([Tests](../architecture/claude-plan.md#tests), [Known gaps](../architecture/claude-plan.md#known-gaps)).
25 changes: 25 additions & 0 deletions docs/adr/0060-one-consent-question-per-user-action.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# ADR 0060: One consent question asks for every host a user action needs, including hosts its background work will reach

- **Status:** Accepted
- **Date:** 2026-10-16

## Context

Egress consent is per host ([ADR 0027](0027-egress-consent-per-host.md)) and asked just in time, on a user action, never on a background call ([ADR 0017](0017-egress-off-by-default.md)). Some actions need several hosts: a ChatGPT sign-in reaches OpenAI's sign-in host and its API; a plugin can sign in on a host of its own; a Claude plan's Claude Code calls `api.anthropic.com` and refreshes its sign-in on `platform.claude.com`. The prompt asked about one host at a time, so a user could allow one and cancel the next, leaving a connection that cannot work.

A host one action needs is not always reached during it. Claude Code refreshes its sign-in in the background, whenever its token is near expiry, at the start of any run; the first run is the model list right after connecting. No dialog can be raised then, so a host left to be asked later is a refusal the user never sees: their Claude plan stops answering once its token expires.

## Decision

- One question per user action, naming every host that action may reach, including a host that work started by it reaches in the background, since that host can never be asked about later.
- A host reached only by a later, separate user action is asked about then, in its own question.
- One Allow switches every host the question named, in one request committed whole (`PUT /egress`): all are allowed or none is.
- Consent stays per host: each is its own row in Settings › Network, switched off alone, and checked on every call.
- The question says what each host receives when they differ: a Claude plan's API host gets what is sent to the model, its sign-in host only the sign-in's refresh.
- Only hosts SurfSense names are ever asked about. A host a program SurfSense runs tries on its own, such as a new one in a future Claude Code, is refused until a release adds it, reviewed and described, to that program's list.

## Consequences

- Connecting a Claude plan, signing in with ChatGPT, or connecting a plugin with its own sign-in host is one question.
- A user cannot allow part of what one action needs from the question itself; they can turn any host off afterwards in Settings › Network.
- A feature whose background work reaches a host its own action did not name fails closed: the host is refused, and the refusal is logged.
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# ADR 0061: Claude Code reaches Anthropic's two hosts, and in the agent also the hosts of the turn's ready plugins

- **Status:** Accepted
- **Date:** 2026-10-16

## Context

[ADR 0057](0057-a-claude-plan-runs-through-the-users-own-claude-code.md) has Claude Code's traffic tunnel "only `api.anthropic.com`". Claude Code also refreshes its sign-in on `platform.claude.com`, so chat runs already allow both, each while the user allows it ([ADR 0060](0060-one-consent-question-per-user-action.md)).

The agent gives Claude Code the thread's plugins. opencode connects to each plugin server itself, with its URL and sign-in header, through the plugin proxy, which passes only a ready plugin's hosts ([agent](../architecture/agent.md#network)). Measured on Claude Code 2.1.301: its MCP traffic to a remote plugin goes through `HTTPS_PROXY`, so a proxy limited to Anthropic refuses the plugin.

The alternative was for SurfSense's tool endpoint to list and forward plugin tools, so Claude Code would reach only Anthropic. SurfSense has no code that forwards an MCP call; it would be a second MCP client beside opencode's, with its own sessions, timeouts and cancellation, to keep plugin tokens out of a process that opencode already gives them to.

## Decision

- A chat run's Claude Code reaches `api.anthropic.com` and `platform.claude.com`, each while its destination is on, and nothing else. A chat run is given no MCP server.
- An agent run's Claude Code reaches the same two hosts and the hosts of the ready plugins, by the check the plugin proxy applies to opencode. It connects to each plugin server itself, as opencode does.
- The two run kinds use separate proxies, so a chat run can never reach a plugin host.
- The MCP servers, with their sign-in headers, are passed to Claude Code in a file only the user can read, never on its command line.

## Consequences

- Claude Code reaches no host the user has not allowed for the same purpose in opencode.
- Claude Code holds the sign-in headers of the plugins it is given for the life of its process, as opencode does.
- A change to how the plugin proxy decides readiness applies to both engines.
- This supersedes ADR 0057's "tunnels only `api.anthropic.com`".
4 changes: 3 additions & 1 deletion docs/adr/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ The Date is the day the decision was first written down. Source links are permal
| 0054 | [SurfSense's own plugins, free and paid, run on one plugin host, with the registry served beside it](0054-surfsenses-plugins-run-on-one-plugin-host.md) | Accepted |
| 0055 | [Verified is a maintainer's judgement, and CI contacts each server a pull request adds](0055-verified-is-a-maintainers-judgement-and-ci-contacts-each-server.md) | Accepted |
| 0056 | [Speech recognition runs on a second audio.cpp server that only the API calls](0056-speech-recognition-runs-on-a-second-audiocpp-server.md) | Accepted |
| 0057 | [A Claude plan is reached through the user's own Claude Code, and SurfSense holds no Claude credential](0057-a-claude-plan-runs-through-the-users-own-claude-code.md) | Accepted |
| 0057 | [A Claude plan is reached through the user's own Claude Code, and SurfSense holds no Claude credential](0057-a-claude-plan-runs-through-the-users-own-claude-code.md) | Accepted, its network decision superseded by 0061 |
| 0058 | [The bundled embedder runs at FP32 on x86-64, upcast in memory, and its vector space is still the pinned FP16 file](0058-the-bundled-embedder-runs-at-fp32-on-x86-64.md) | Accepted |
| 0059 | [A born-digital PDF is read from its own text layer, and Docling reads only a PDF with a page that needs it](0059-a-born-digital-pdf-is-read-from-its-text-layer.md) | Accepted |
| 0060 | [One consent question asks for every host a user action needs, including hosts its background work will reach](0060-one-consent-question-per-user-action.md) | Accepted |
| 0061 | [Claude Code reaches Anthropic's two hosts, and in the agent also the hosts of the turn's ready plugins](0061-claude-code-reaches-anthropic-and-in-the-agent-the-turns-plugins.md) | Accepted |
Loading
Loading