ScepticAgent internal architecture reference. Use whenever the user asks how the extension works, wants to add a new AI provider, add a new highlight category, debug communication between components, understand the agent loop or streaming, or work with provider routing and the Gemini CORS proxy.
Scanned 9/1/2026
Install to Claude Code
npx -y skills add Maros112358/scepticagent --skill scepticagent-architecture --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Scepticagent Architecture?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/maros112358-scepticagent-architecture)More formats (shields.io, HTML) on the badges page.
---
name: scepticagent-architecture
description: ScepticAgent internal architecture reference. Use whenever the user asks how the extension works, wants to add a new AI provider, add a new highlight category, debug communication between components, understand the agent loop or streaming, or work with provider routing and the Gemini CORS proxy.
---
## Communication flow
```
Side Panel (app.js)
│ chrome.runtime.connect("agent") — one port per request
▼
Service Worker (background.js)
│ fetch() to provider API
▼
AI Provider (Anthropic / OpenAI / Gemini)
```
Two parallel port connections fire on page open via `Promise.all`:
1. `streamViaPort("analyze")` — full analysis with agent loop
2. `quickHighlightPage()` — fast highlight scan
## Port message types
| Message | Meaning |
|---------|---------|
| `{ type: "status", message }` | Progress label shown in UI |
| `{ type: "tool_call", name, label }` | Tool being invoked |
| `{ type: "text_delta", text }` | Streamed text chunk |
| `{ type: "highlight_item", highlight, index }` | One highlight, emitted in real-time |
| `{ type: "highlights_result", summary, categorySummaries }` | Final highlights metadata |
| `{ type: "done" }` | Stream complete |
| `{ type: "error", message }` | Error — stops streaming |
## Agent loop (Claude only)
`runAgentLoop` / `runHighlightsAgentLoop` — up to 12 iterations (4 for highlights).
Stop reasons:
- `end_turn` → break
- `pause_turn` → continue (server-side web_search still running)
- `tool_use` → run `fetch_url`, re-submit with results
Tools: `web_search_20250305` (server-side, Anthropic-hosted), `fetch_url` (client-side, `fetchUrl()` in background.js).
## Streaming parser
`parseAgentStream(response, port, onTextDelta?)` reads the Anthropic SSE stream.
Optional `onTextDelta` callback intercepts text chunks — used by highlights to extract JSON objects in real-time via `extractCompleteHighlights()`.
## Real-time highlight extraction
`extractCompleteHighlights(buffer, fromPos)` — brace-depth character scanner that finds complete `{"phrase":...}` JSON objects in a mid-stream buffer without waiting for the full response. Returns `{ highlights, nextPos }`.
## Provider routing
`getProviderConfig()` reads `provider`, `${p}Key`, `${p}Model` from `chrome.storage.local`.
- `"anthropic"` → full agent loop with tools
- `"openai"` → `runOpenAIStream` — SSE streaming, no tools
- `"gemini"` → `runGeminiStream` + `geminiProxyFetch` — non-streaming, proxied through content script
## Gemini CORS proxy
Service workers send `Origin: chrome-extension://[id]` which Google's API rejects.
Fix: `geminiProxyFetch()` sends the request to the content script via `chrome.tabs.sendMessage`. The content script fetches from the page's `http/https` origin, which Google accepts.
Use `Content-Type: text/plain` + key as URL query param to avoid CORS preflight (simple request).
## Settings storage keys
| Key | Description |
|-----|-------------|
| `provider` | `"anthropic"` / `"openai"` / `"gemini"` |
| `anthropicKey` / `openaiKey` / `geminiKey` | API keys |
| `anthropicModel` / `openaiModel` / `geminiModel` | Selected model |
| `apiKey` | Legacy Anthropic key (still read for backwards compat) |
## Adding a new provider — checklist
- [ ] Add `const XXX_API_BASE` constant in `background.js`
- [ ] Add to `getProviderConfig()` key/model maps
- [ ] Implement `runXxxStream(port, apiKey, model, systemText, messages, signal)`
- [ ] Branch in `handleAnalyze`, `handleChat`, `handleHighlights`
- [ ] Add provider tab to `options/index.html` and logic to `options/app.js`
- [ ] Add API domain to `host_permissions` in `manifest.json`
- [ ] Update `CLAUDE.md` and `README.md`
## Adding a new highlight category — checklist
- [ ] Add to `LABELS` object and add `.ra-hl-{category}` CSS rule in `content.js`
- [ ] Add to `CAT_META` in `sidepanel/app.js`
- [ ] Add `.scan-pill.{cat}`, `.hl-dot.{cat}`, `.hl-item-dot.{cat}`, `.summary-bullet-{cat}` in `sidepanel/style.css`
- [ ] Add scan pill and legend entry in `sidepanel/index.html`
- [ ] Add category description in `buildHighlightsPrompt()` in `background.js`
- [ ] Update `CLAUDE.md` and `README.md`
## Known limitations
- **Gemini**: no streaming (`generateContent` only), no tool use, no real URLs in `source_url`
- **OpenAI**: no tool use, single-turn only
- **Highlights agent loop**: Claude only — non-Claude providers use `handleHighlightsSimple`
- **Page content**: truncated to 8000–10000 chars before sending to AI
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!