From 397b9c306a6f52b8e58f6a49451ea9faa4050e0c Mon Sep 17 00:00:00 2001 From: Aaron Sequeira Date: Tue, 27 Oct 2026 01:50:31 +0530 Subject: [PATCH 1/2] docs: simplify guides and runtime packaging --- .github/pull_request_template.md | 101 +-------- CHANGELOG.md | 2 + CONTRIBUTING.md | 353 ++++++------------------------ README.md | 192 +++++++--------- docs/DEVELOPMENT.md | 338 +++++++++++----------------- docs/INTEGRATION-REVIEW.md | 155 ++++--------- docs/SECURITY.md | 331 ++++++++-------------------- docs/ci-modernization-evidence.md | 137 +++++------- docs/product/PRODUCT.md | 6 +- docs/product/PROJECT-STATUS.md | 116 +++------- docs/security-testing-evidence.md | 196 +++++++---------- package.json | 12 +- 12 files changed, 591 insertions(+), 1348 deletions(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index fe9330e..c185b22 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,102 +1,17 @@ ## Description -Provide a brief description of the changes made in this pull request. + -## Type of Change +## Validation -- [ ] Bug fix (non-breaking change which fixes an issue) -- [ ] New feature (non-breaking change which adds functionality) -- [ ] Breaking change (fix or feature that would cause existing functionality to change) -- [ ] Documentation update -- [ ] Code refactoring -- [ ] Performance improvement -- [ ] Test coverage improvement + -## Related Issues +## Risks and rollback -- Closes #(issue number) -- Fixes #(issue number) -- Relates to #(issue number) - -## Changes Made - -### Core Changes -- [ ] List specific changes made to core functionality -- [ ] Include any architectural changes -- [ ] Note any new dependencies added - -### UI/UX Changes -- [ ] Describe any user interface changes -- [ ] Include screenshots if applicable -- [ ] Note any accessibility improvements - -### API Changes -- [ ] Document any API changes -- [ ] Note breaking changes -- [ ] Update version numbers if needed - -## Testing - -### Automated Testing -- [ ] Unit tests pass (`npm test`) -- [ ] E2E tests pass (`npm run test:e2e`) -- [ ] Coverage threshold met (`npm run test:coverage`) -- [ ] Linting passes (`npm run lint`) -- [ ] Type checking passes (`npm run typecheck`) - -### Manual Testing -- [ ] Extension loads without errors in Chrome -- [ ] Extension loads without errors in Edge -- [ ] Context menus function correctly -- [ ] Popup opens and functions correctly -- [ ] Background service worker initializes -- [ ] Content scripts inject properly -- [ ] AI providers respond correctly -- [ ] Error handling works as expected - -### Browser Compatibility -- [ ] Chrome (version 88+) -- [ ] Edge (version 88+) -- [ ] Tested in incognito/private mode - -## Security Considerations - -- [ ] Input validation implemented -- [ ] XSS prevention measures in place -- [ ] API keys handled securely -- [ ] Content Security Policy updated if needed -- [ ] No sensitive data exposed in logs - -## Performance Impact - -- [ ] No significant performance degradation -- [ ] Memory usage is reasonable -- [ ] API calls are optimized -- [ ] Caching implemented where appropriate -- [ ] Bundle size impact is minimal - -## Documentation - -- [ ] README.md updated if needed -- [ ] CONTRIBUTING.md updated if needed -- [ ] Code comments added for complex logic -- [ ] JSDoc comments added for new functions -- [ ] CHANGELOG.md updated + ## Screenshots - - -## Additional Notes - - - -## Checklist - -- [ ] I have performed a self-review of my code -- [ ] I have commented my code, particularly in hard-to-understand areas -- [ ] I have made corresponding changes to the documentation -- [ ] My changes generate no new warnings -- [ ] I have added tests that prove my fix is effective or that my feature works -- [ ] New and existing unit tests pass locally with my changes -- [ ] Any dependent changes have been merged and published \ No newline at end of file + diff --git a/CHANGELOG.md b/CHANGELOG.md index 383bf07..9f5673f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +- Replace fictional contribution examples, consolidate setup and validation + guidance, and exclude development configs from the npm package (#35). - Pin verified current CI action releases, select Ubuntu 26.04 explicitly, and avoid retaining checkout credentials while preserving the Node test matrix (#32). - Preserve saved choices when settings reads fail: suppress optional notices and diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index cec4a46..760e9ab 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,332 +1,113 @@ # Contributing to GenAI Browser Tool -
- -![Contributors Welcome](https://img.shields.io/badge/contributors-welcome-brightgreen.svg) -![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg) -![Good First Issue](https://img.shields.io/badge/-good%20first%20issue-blueviolet) - -[Quick Start](#quick-start) • [Dev Setup](#development-setup) • [Commit Rules](#commit-guidelines) • [Code Style](#code-style-guidelines) • [Testing](#testing-requirements) • [PR Process](#pull-request-workflow) - -
- -## Table of Contents - -
-Expand - -- [Quick Start](#quick-start) -- [Development Setup](#development-setup) -- [Project Structure](#project-structure) -- [Branching Model](#branching-model) -- [Commit Guidelines](#commit-guidelines) -- [Code Style Guidelines](#code-style-guidelines) -- [Security & Privacy](#security--privacy) -- [Testing Requirements](#testing-requirements) -- [Pull Request Workflow](#pull-request-workflow) -- [Review Process](#review-process) -- [Release Process](#release-process) -- [Issue Triage](#issue-triage) -- [Community Expectations](#community-expectations) - -
- ## Quick Start -1. Fork the repo and create your branch from `main`: - ```bash - git clone https://github.com/YOUR_USERNAME/GenAI-Browser-Tool - cd GenAI-Browser-Tool - git checkout -b feature/awesome-change - ``` -2. Install dependencies and prepare hooks: - ```bash - npm ci - npm run prepare - ``` -3. Load the unpacked extension from the **project root** at `chrome://extensions`. - No build step is needed — the manifest points at the source files. -4. Write tests, run `npm run verify`, then open a PR. +Fork the repository, clone your fork, and create a short-lived branch from current +`main`. The extension loads directly from the repository root; see the +[installation guide](README.md#start-here). ## Development Setup -### Prerequisites - -- Node.js >= 20.19, npm >= 10 -- Chrome >= 116 (MV3 service worker modules) -- Git +Use Node >=20.19, npm >=10, and Chrome >=116. Run `npm ci` to install +development tools and prepare Git hooks. Provider keys and model overrides belong +in the extension's options page, not an `.env` file. -### Configuration - -There is no `.env` file. Provider selection, API keys, and model overrides all -live on the extension's options page and are stored in `chrome.storage.sync`. - -### Common scripts - -```bash -npm run verify # lint + typecheck + test — run before every PR -npm test # Unit and integration tests (Vitest) -npm run test:e2e # Playwright E2E -npm run lint # ESLint -npm run typecheck# tsc --noEmit over shipped source (must be zero errors) -npm run format # Prettier -``` - -There is no build step. The repository root is the loadable extension, and -packaging is a zip of the root. Requires Node 20.19 or newer. +[Local validation](docs/DEVELOPMENT.md#local-validation) is the authoritative +command list. Run `npm run verify` before a PR and the coverage/browser checks +appropriate to the change. Avoid repository-wide formatting unrelated to your work. ## Project Structure -``` -GenAI-Browser-Tool/ -├── background.js # MV3 service worker — message router, all AI calls -├── content.js # content script, read-only DOM extraction -├── popup.html # popup markup (logic in scripts/popup-main.js) -├── options.html/.css/.js # settings UI -├── core/ -│ ├── configuration-manager.js # settings + AI client construction -│ └── tasks.js # prompt construction and response parsing -├── providers/ -│ └── ai-client.js # one fetch client, shaped per provider -├── scripts/popup-main.js # popup logic -├── services/ # storage, notifications -├── utils/ # logger, validation -├── styles/ # popup CSS -├── tests/ # unit, integration, and e2e tests -├── docs/ # DEVELOPMENT.md, SECURITY.md -└── manifest.json # MV3 manifest -``` +See the [source map and message contract](docs/DEVELOPMENT.md#source-map). +All provider calls go through the worker and `providers/ai-client.js`. -### Adding a provider - -Add one entry to `PROVIDERS` in `providers/ai-client.js` (url, headers, body, -parse), add its host to `host_permissions` in `manifest.json`, and add the key -and model inputs to `options.html`. Everything else — options wiring, status -badge, and all seven tasks — picks it up automatically. Add request-shaping -tests alongside the existing ones in `tests/providers/ai-client.test.js`. +To add a provider, update `PROVIDERS`, its manifest host permission, and the +options key/model inputs. Exercise request shaping and response/error handling in +[the provider tests](tests/providers/ai-client.test.js). ## Branching Model -Create short-lived feature, fix, documentation, or maintenance branches from -current `main`, and target the final integration PR at `main`. This repository -does not use a long-lived `develop` branch. A stacked PR may target an unmerged -feature branch; retarget it at `main` when its base lands. Branch protection is -a repository setting, not a guarantee made by this guide. +Target final integration PRs at `main`; there is no long-lived `develop` branch. +A stacked PR may target an unmerged feature branch. Retarget when its base lands; +the [ancestry check](docs/DEVELOPMENT.md#pull-requests-and-packaging) describes +what CI detects. ## Commit Guidelines -Follow Conventional Commits. Examples: - -``` -feat(provider): add Gemini fallback when OpenAI is rate-limited -fix(content): sanitize HTML before summarization to avoid XSS -perf(cache): memoize provider health checks -docs(readme): add manual install steps for Firefox -chore(ci): add CodeQL security scan -``` - -Rules: - -- Imperative mood, max 50 chars subject, no trailing period -- Optional body wraps at 72 cols; explain motivation, approach, tradeoffs -- Reference issues: `Closes #123`, `Refs #456` -- Breaking changes in footer: `BREAKING CHANGE: ...` +Use Conventional Commits, for example `fix(content): handle empty pages` or +`docs(readme): clarify unpacked installation`. Keep the subject imperative, +at most 50 characters, without a trailing period. Wrap an optional body at 72 +columns, explain consequential trade-offs, and link issues. Mark incompatible +changes with a `BREAKING CHANGE:` footer. ## Code Style Guidelines -### JavaScript/TypeScript - -- Use TypeScript types in new/changed files when possible -- Prefer pure functions, avoid side effects in providers/services -- Validate message boundaries and escape rendered output; see [the security guide](docs/SECURITY.md) -- No direct DOM injection; sanitize with DOMPurify before rendering -- Avoid long functions (>40 lines). Extract helpers. - -Example patterns: - -```ts -// Typed provider request -export interface AIRequest { - kind: 'summary' | 'qa' | 'translate' | 'analyze'; - text: string; - locale?: string; -} -export interface AIResponse { - content: string; - tokens?: number; - latencyMs?: number; - sources?: string[]; -} - -export async function summarize(req: AIRequest): Promise { - if (req.kind !== 'summary') throw new Error('invalid kind'); - // ... impl - return { content: result.text, tokens: result.usage?.total_tokens }; -} -``` - -### UI/UX - -- Keep popup UI responsive and accessible (ARIA labels, tab order) -- Respect prefers-color-scheme; ensure contrast and keyboard navigation -- No blocking operations on UI thread; use async boundaries and spinners - -### Security & Privacy - -- Do not log API keys or user content -- Treat `chrome.storage.sync/local` as plaintext; keep API keys out of logs and exports -- Enforce CSP in manifest; never eval or inline scripts -- Strip PII before sending to external APIs where feasible +Follow adjacent JavaScript and JSDoc conventions; ESLint, TypeScript's +`--noEmit` check, and Prettier are already configured. Prefer cohesive helpers +and test observable behavior. Keep UI controls keyboard accessible and show +progress/errors for asynchronous actions. ## Security & Privacy -All contributions must comply with: - -- Manifest V3 policies (no remote code, background service worker) -- Chrome Web Store / Edge Add-ons policies -- Minimum-permission principle - -Checklist before PR: - -- [ ] No secrets in code or history -- [ ] Inputs validated and sanitized -- [ ] External requests over HTTPS with proper hosts -- [ ] Respect user opt-outs and incognito isolation +Follow the [security policy](docs/SECURITY.md). Validate message boundaries, +preserve the manifest CSP, and escape untrusted output before adding the fixed +Markdown subset. Rendering uses a local escaping function, with no DOMPurify. +Keep API keys out of logs and exports, and user content out of logs; storage is plaintext at the +API level. Review permissions, provider hosts, and privacy opt-outs when changing +those paths. Report vulnerabilities privately using the policy's contact. ## Testing Requirements -CI enforces coverage over all shipped JavaScript: 88% lines, 85% statements, -85% functions, and 78% branches, plus a 75% branch floor for `background.js`. -See [vitest.config.js](vitest.config.js). Additional goals: - -- Unit: ≥ 80% lines on changed modules -- E2E: happy paths for popup, summary, Q&A, translate +Use the existing Vitest and Playwright suites. For example, this case in +[rendering.test.js](tests/popup/rendering.test.js) exercises the shipped renderer: -Examples: - -```ts -// Vitest unit example -import { describe, it, expect } from 'vitest'; -import { sanitize } from '@/services/security-manager'; - -describe('security', () => { - it('removes scripts from HTML', () => { - expect(sanitize('')).not.toMatch(/onerror/); - }); +```js +it('escapes tags in model output before adding markup', () => { + const html = renderMarkdown(''); + expect(html).not.toContain(' { - await page.goto('https://example.com'); - // open extension UI per your test harness - await expect(page.locator('.genai-popup')).toBeVisible(); - await page.click('#summarize'); - await expect(page.locator('.summary-result')).toHaveText(/summary/i, { - timeout: 10000, - }); -}); -``` - -CI checks must pass: lint, typecheck, unit, e2e (where configured). +Run that file with `npx vitest run tests/popup/rendering.test.js`. +For browser tests, reuse the [persistent Chromium fixture](tests/e2e/fixtures.js) +and [workspace examples](tests/e2e/learning-workspace.spec.js). +Coverage thresholds live in [vitest.config.js](vitest.config.js); +[validation limits](docs/DEVELOPMENT.md#local-validation) distinguish intercepted +provider tests from manual/live checks. ## Pull Request Workflow -Target `main` for the final integration PR. Stacked PRs can target a feature -branch while it has unmerged work; CI rejects a base already contained in the -default branch and asks you to retarget. See -[the development guide](docs/DEVELOPMENT.md#pull-requests-and-packaging) for the -check's scope and packaging rules. - -Pre‑flight: - -- [ ] Lint, typecheck, tests pass locally -- [ ] Updated README/docs for user‑facing changes -- [ ] Added/updated tests -- [ ] Coverage did not regress (`npm run test:coverage` enforces a threshold) - -PR Description Template: - -```markdown -## Summary - -What changed and why. - -## Changes - -- ... - -## Tests - -- Unit: -- E2E: - -## Screenshots - -(If UI changed) - -## Risks & Rollback - -Potential impact and how to revert. - -## Related - -Closes #123, Refs #456 -``` - -Labels: - -- `bug`, `enhancement`, `documentation`, `dependencies`, `security`, `breaking change`, `good first issue` +Use the [PR template](.github/pull_request_template.md). Describe the problem, +linked issue, actual validation, gaps, and material risks. Include screenshots +when the UI changes. Add regression coverage for behavior changes and update the +relevant canonical guide. ## Review Process -For reviewers: - -- Focus on correctness, security, privacy, and maintainability -- Suggest concrete improvements; prefer questions over directives -- Approve if concerns are addressed even if further polish is possible - -For authors: - -- Respond to all comments -- Keep commits clean (squash before merge unless history is valuable) -- Provide benchmarks when touching performance‑critical paths +Review correctness, security, privacy, compatibility, and maintainability. +Support findings with concrete behavior and tight source/test references. +Authors should address each actionable comment and provide measurements for +performance claims. Squash before merge unless the commit history is useful. ## Release Process -We use SemVer and align `manifest.json` version with `package.json`. - -Steps: - -1. Update versions: `npm version patch|minor|major` -2. Update CHANGELOG.md -3. Smoke test: `npm run verify && npm run test:e2e` -4. Load the repository root unpacked in Chrome and summarize a real page — - `activeTab` is granted only on a real toolbar click, which the E2E suite - cannot perform -5. Tag and push: `git push --follow-tags` -6. Download the ZIP from the CI `package` job's Actions artifact; create a GitHub - release separately if distributing it +1. Align versions in `package.json`, `package-lock.json`, and `manifest.json`. + `npm version` does not update the Chrome manifest. +2. Update [CHANGELOG.md](CHANGELOG.md) and run the + [release checks and packaging workflow](docs/DEVELOPMENT.md#pull-requests-and-packaging). +3. Load the extension in Chrome and summarize a real page after a toolbar click; + automated tests cannot grant that exact `activeTab` interaction. +4. Tag/push an approved release and distribute the inspected CI ZIP through a + separate GitHub release. A merge is not publication to the Chrome Web Store. ## Issue Triage -Triage labels we use: - -- `needs repro`, `blocked`, `security`, `performance`, `help wanted`, `good first issue` - -When filing a bug, include: - -- Repro steps, expected vs actual, console logs (redacted), browser version, OS, extension version. +Report bugs with reproduction steps, expected/actual results, extension/browser +versions, OS, and redacted logs. Link existing issues rather than duplicating +them; keep security disclosures private. ## Community Expectations -- Be kind, be specific, be constructive. -- Follow our [Code of Conduct](CODE_OF_CONDUCT.md). -- Respect privacy and ethical AI usage in proposals and code. - ---- - -Thank you for contributing to GenAI Browser Tool. Your improvements help thousands browse smarter and safer. +Be specific and constructive, respect privacy, and follow the +[Code of Conduct](CODE_OF_CONDUCT.md). diff --git a/README.md b/README.md index 1cc8103..48db2c5 100644 --- a/README.md +++ b/README.md @@ -1,148 +1,118 @@ # GenAI Browser Tool -Read a page, ask questions with source passages, and turn the material into a -study guide or a quiz. A Chrome 116+ extension using the AI provider and API key -you choose. Page text goes directly to that provider; there is no application -server or telemetry. +Read a page, ask questions with source passages, and turn it into a study guide +or quiz. This Chrome 116+ extension uses the AI provider and key you choose. +Page text goes directly to that provider; there is no application server or telemetry. ![Learning workspace with a question and its source passage](docs/product/images/learning-workspace.png) -The screenshot is from an automated browser test with an intercepted AI response. -It demonstrates the interface, not live model quality. +The screenshot uses an intercepted test response; it demonstrates the interface, +not live model quality. ## Start here +Clone or download this repository: + ```bash git clone https://github.com/aaron-seq/GenAI-Browser-Tool.git -cd GenAI-Browser-Tool -npm ci ``` 1. Open `chrome://extensions`, enable **Developer mode**, and choose **Load - unpacked** → the repository root. No build step is needed. -2. In options, select a provider and enter its API key. **Test connection** sends - one small request using the entered key and model. Settings save automatically - when a field changes; connection testing is optional. -3. Open an article and click the extension. Check the word count and page title, - then generate a summary or ask a question. -4. Choose **Open workspace** for a full-page reading area that stays open. You can - also import a UTF-8 `.txt` or `.md` document there. Importing sends no AI request. -5. Use **Make a study guide** or **Quiz me** in Chat, then ask follow-up questions. - Expand the sources to check the evidence used for each answer. - -Development needs Node >=20.19 and npm >=10. The extension ships no runtime -dependencies. It is currently installed unpacked, not published in the Web Store. + unpacked** → the repository root containing `manifest.json`. No build or + dependency installation is needed to use the extension. +2. In options, select a provider and enter its API key. Fields save automatically. + **Test connection** optionally sends a small request with the entered key/model. +3. Open an article, click the extension, and check its title and word count. + Generate a summary or ask a question. +4. Choose **Open workspace** for a persistent reading tab. Import UTF-8 `.txt` + or `.md` files there; importing alone sends no provider request. +5. In Chat, choose **Make a study guide** or **Quiz me**, ask follow-up questions, + and expand the source passages to check the evidence. + +Installation is currently unpacked; there is no published Web Store release. ## What works -| Action | Behavior | -| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Summary | Key points, TL;DR, executive, or technical; four lengths. Matching saved results are reused without another provider call. | -| Chat and study prompts | Search passages across the extracted document, send up to six passages to the model, and show their source labels and excerpts. | -| Remember this document | Restore the latest summary and up to 30 completed exchanges for the same URL and unchanged text. **Clear this chat** deletes that document's saved exchanges. | -| Import text | Workspace accepts `.txt`/`.md`, up to 2,000,000 characters and 8 MB. Reimporting the same filename and text restores its thread. | -| Translate | Choose source and target languages; preferences persist. Translate the page or an opening excerpt. | -| Analyze | Sentiment, insights, entities, and topic tags; local readability score. | -| Tools | Local word counts, reading time, headings, links, and JSON export of saved history/settings without API keys. | -| Quick actions | Right-click a selection/page; `Ctrl+Shift+S` (`Command+Shift+S` on Mac) requests a quick page summary. | +- **Summary:** four styles and four lengths; matching saved results avoid another + provider call. +- **Chat:** local keyword retrieval selects up to six passages across the document. + Answers display source labels and excerpts. +- **Document memory:** restore the latest summary and up to 30 completed exchanges + for the same URL and unchanged text. **Clear this chat** removes its exchanges. +- **Import:** text/Markdown up to 2,000,000 characters and 8 MB; the same filename + and text identify a returning imported document. +- **Translate/analyze:** saved language preferences, sentiment, insights, entities, + and tags. Local tools provide readability, counts, headings, links, and JSON + export of history/settings without keys. +- **Quick actions:** right-click a page/selection or use `Ctrl+Shift+S` + (`Command+Shift+S` on Mac) for a quick page summary. ### Long documents and speed -Chat uses local **BM25 keyword retrieval** over the document, including its end. -The displayed passage count shows how much evidence reached the model. Broad -questions without matching words use representative passages from across the -document. This is retrieval followed by generation, not model training. Synonyms, -some languages, and questions requiring all chapters can exceed its recall. - -Summaries process up to **eight sections of at most 24,000 characters**, with up -to three section requests in flight and one final merge: at most nine requests -before retries. Coverage is displayed, including any text omitted. A matching -summary cache includes the document, provider, model, style, and length. +Summaries process up to **eight sections of 24,000 characters**, with three +section requests in flight and a final merge (up to nine requests before retries). +Coverage notices identify omitted text. Translation, other analyses, and quick +actions still use the first 24,000 characters; see [#17](https://github.com/aaron-seq/GenAI-Browser-Tool/issues/17). -Translation, other AI analyses, and quick context-menu actions still use the -first 24,000 characters. Popup translation/analysis reports partial coverage. -The quick notification actions are best for short text; use the Summary tab for -sectioned long-page summaries. Remaining long-task work is tracked in -[#17](https://github.com/aaron-seq/GenAI-Browser-Tool/issues/17). - -AI response time depends on your provider, model, network, and document. The -[local benchmark](docs/product/RESEARCH.md#performance-evidence) measures retrieval -only; it does not establish instant book comprehension. PDF, EPUB, scanned books, -and OCR are [planned separately (#26)](https://github.com/aaron-seq/GenAI-Browser-Tool/issues/26). +Chat uses local BM25 keyword retrieval, not model training. Synonyms, some languages, +and questions requiring every chapter can exceed its recall. The +[retrieval benchmark](docs/product/RESEARCH.md#performance-evidence) does not measure +provider latency or establish instant book comprehension. PDF/EPUB/OCR are +[planned separately](https://github.com/aaron-seq/GenAI-Browser-Tool/issues/26). ## Providers and privacy -| Provider | Default model | Key | -| --------- | ----------------------- | --------------------------------------------------- | -| Anthropic | `claude-haiku-5-5` | [Claude Console](https://console.anthropic.com) | -| OpenAI | `gpt-6-luna` | [API keys](https://platform.openai.com/api-keys) | -| Google | `gemini-3.5-flash-lite` | [AI Studio](https://aistudio.google.com/app/apikey) | - -Model IDs are configurable. Defaults were checked against official documentation; -live account access, cost, latency, and answer quality have not been measured. -See [provider evidence and selection](docs/product/RESEARCH.md#provider-selection). -AI actions require your key and may incur provider charges. Connection testing -does not gate saving; without a key, local extraction/statistics still work. - -API keys are in `chrome.storage.sync`, which is not an encrypted secret store. -History is local to the browser profile: at most 1,000 summaries and 500 completed -exchanges, with daily cleanup of entries older than 90 days. **Save history** in -options disables new saves, cache reuse, and thread restoration. It does not erase -existing records; clear a document's chat before disabling it if needed. Imported -full documents and retrieval indexes are not persisted, but saved answers and -source excerpts may contain sensitive text. JSON export includes saved material. -If local storage fills, the generated result stays visible with a save warning; -copy it before closing the tab. +Choose Anthropic, OpenAI, or Google and optionally override the model in options. +See [default models and provider evidence](docs/product/RESEARCH.md#provider-selection). +AI requests require your key and may incur charges. Local extraction/statistics +work without one; live account access and answer quality have not been evaluated. + +Keys live in `chrome.storage.sync`, which is not an encrypted secret store. +History stays in the browser profile: up to 1,000 summaries and 500 exchanges, +with daily cleanup of entries older than 90 days. **Save history** off disables +new saves, summary-cache reuse, and restoration; it does not erase old records. +Saved answers/source excerpts and JSON exports can contain sensitive text. +Full imported documents and retrieval indexes are not persisted. + +Options apply immediately. Disabling context menus removes right-click actions; +the shortcut remains available. Disabling notifications suppresses desktop +notices, including shortcut results; popup/workspace output remains visible. +Unreadable preferences suppress optional notices/history and cannot be replaced +with defaults by an update. If saving fails, copy the visible result before closing. See [security and data handling](docs/SECURITY.md). -An optional private semantic retrieval service is being researched in -[#27](https://github.com/aaron-seq/GenAI-Browser-Tool/issues/27). No backend, local -model, or semantic embedding service is installed by this release. - -Options apply immediately: disabling context menus removes the right-click -actions; the keyboard shortcut remains available. Disabling notifications -suppresses desktop notices, including errors and shortcut results. Popup and -workspace results remain visible. See the [preference fix](https://github.com/aaron-seq/GenAI-Browser-Tool/issues/31). - -If saved settings cannot be read, the extension reports the failure instead of -replacing your choices with defaults. It suppresses desktop notices and optional -history writes. A result already generated stays visible with a save warning; -copy it before closing. If Settings cannot load, reload that page to retry. +No semantic backend or local model is installed. Optional private retrieval is +[research work](https://github.com/aaron-seq/GenAI-Browser-Tool/issues/27). ## Troubleshooting -| Symptom | Next step | -| ----------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | -| No API key / 401 / 403 | Check the selected provider, key, model access, and **Test connection** in options. | -| 429 / provider unavailable | Check provider quota; transient failures retry twice. Authentication/configuration errors and timeouts are not retried. | -| Cannot read a page | Browser pages, the Web Store, and PDF viewers are restricted. Dynamic pages may require reloading after the extension is installed. | -| Changed page starts a new chat | Expected: text and URL identify a document, so another version cannot inherit unrelated answers. | -| Summary is incomplete | Read the coverage notice. Documents over the eight-section cap need smaller parts. | -| Changes are missing after an update | Reload at `chrome://extensions`, then reopen the popup/workspace. | +| Symptom | Next step | +| ------------------------------ | ------------------------------------------------------------------------------------------------------ | +| Missing key / 401 / 403 | Check the selected provider, key, model access, and **Test connection**. | +| 429 / provider unavailable | Check quota. Transient failures retry twice; authentication/configuration errors and timeouts do not. | +| Cannot read a page | Browser pages, the Web Store, and PDF viewers are restricted. Reload dynamic pages after installation. | +| Changed page starts a new chat | URL and text identify a document; changed versions start another thread. | +| Incomplete summary | Read the coverage notice; split documents beyond the eight-section cap. | +| Settings cannot load | Reload Settings to retry; do not assume defaults reflect saved choices. | +| Update is missing | Reload the extension at `chrome://extensions`, then reopen its UI. | ## Develop and contribute +Development needs Node >=20.19 and npm >=10: + ```bash +npm ci npm run verify -npm run test:coverage npx playwright install chromium npm run test:e2e -npm audit --audit-level=high -npm run benchmark:retrieval ``` -Browser tests load the actual extension and intercept provider requests; no real -API key is required. CI checks Node 20/22/24, browser behavior, and an explicitly -allowlisted runtime ZIP. See [development](docs/DEVELOPMENT.md) and -[contributing](CONTRIBUTING.md). - -The dedicated [product brief](docs/product/PRODUCT.md) records the goal, delivered -behavior, and next steps. Supporting research and verification are in -[RESEARCH.md](docs/product/RESEARCH.md); the supplied instructions are preserved -in [ENGINEERING-BRIEF.md](docs/product/ENGINEERING-BRIEF.md). - -Issues: [GitHub](https://github.com/aaron-seq/GenAI-Browser-Tool/issues). -Current work: [GitHub dashboard](https://github.com/users/aaron-seq/projects/6) -(owner access), [continuation checkpoint](docs/product/PROJECT-STATUS.md), and -[settings-failure evidence](docs/security-testing-evidence.md). -License: [MIT](LICENSE). +Browser tests load the actual extension with intercepted provider responses. +See [development](docs/DEVELOPMENT.md) for coverage, audit, benchmark, CI, and +packaging commands, and [contributing](CONTRIBUTING.md) for change/review rules. + +[Product brief](docs/product/PRODUCT.md) · [Research](docs/product/RESEARCH.md) · +[Supplied engineering brief](docs/product/ENGINEERING-BRIEF.md) · +[Current work](docs/product/PROJECT-STATUS.md) · +[GitHub issues](https://github.com/aaron-seq/GenAI-Browser-Tool/issues) · +[Private dashboard](https://github.com/users/aaron-seq/projects/6) · [MIT license](LICENSE) diff --git a/docs/DEVELOPMENT.md b/docs/DEVELOPMENT.md index 4b8a03d..52e37b1 100644 --- a/docs/DEVELOPMENT.md +++ b/docs/DEVELOPMENT.md @@ -1,198 +1,120 @@ # Development Guide -This guide provides detailed information for developers working on the GenAI Browser Tool extension. - ## Architecture Overview -Every AI call originates in the service worker. The popup and the options page -never talk to a provider, and the content script has no network access at all — -so there is exactly one place where a request can be shaped, retried, or leak a -key. - -```mermaid -flowchart TD - popup["popup.html
scripts/popup-main.js"] - options["options.html
options.js"] - menus["Context menus
Ctrl+Shift+S"] - bg["background.js
service worker — message router"] - content["content.js
read-only DOM extraction"] - config["core/configuration-manager.js
chrome.storage.sync"] - tasks["core/tasks.js
prompt construction"] - client["providers/ai-client.js
one fetch client, shaped per provider"] - store["services/storage-service.js
chrome.storage.local history"] - api["Provider HTTPS API"] - - popup -->|chrome.runtime.sendMessage| bg - options -->|writes settings| config - options -->|connection test| bg - menus --> bg - bg -->|chrome.tabs.sendMessage| content - content -->|page text| bg - bg --> config - bg --> tasks - config -->|builds a client for
the selected provider| client - tasks -->|system + user prompt| client - client -->|fetch, with retry| api - bg --> store -``` - -### Request lifecycle: one summary +The repository root is a loadable MV3 extension with no build step or runtime +dependencies. All provider calls originate in the worker; the content script +only reads the page. ```mermaid -sequenceDiagram - participant U as User - participant P as Popup - participant B as background.js - participant C as content.js - participant A as Provider API - - U->>P: Click "Generate Summary" - P->>B: EXTRACT_PAGE_CONTENT { tabId } - B->>C: chrome.tabs.sendMessage - C-->>B: title, mainText, headings - B-->>P: page content - P->>B: GENERATE_CONTENT_SUMMARY { content, style, length } - B->>B: chunkContent() — split if over 24,000 chars - - alt page fits one request - B->>A: one completion - A-->>B: summary - else long page (map-reduce, 3 sections in flight) - loop each section, capped at 8 - B->>A: summarize section - A-->>B: section notes - end - B->>A: merge the notes - A-->>B: whole-page summary - end - - B->>B: save to local history - B-->>P: text, provider, model, sections, droppedChars - P->>U: rendered summary + coverage statement +flowchart LR + UI["Popup / workspace / options"] -->|runtime messages| Worker["background.js"] + Menu["Menus / shortcut"] --> Worker + Worker -->|tab message| Content["content.js"] + Worker --> Config["ConfigurationManager"] + Worker --> Tasks["core/tasks.js"] + Tasks --> Client["providers/ai-client.js"] + Config --> Client + Client -->|HTTPS| API["Selected provider"] + Worker --> History["StorageService"] ``` -A failure at any provider step is returned with its own message and an error -code (`MISSING_API_KEY`, `AUTH_ERROR`, `TIMEOUT`, …). Nothing degrades into a -plausible-looking fake result, and a section that fails after retries fails the -whole summary rather than producing a partial one presented as complete. +A summary extracts page text, checks the saved-result key, then either makes one +request or summarizes up to eight 24,000-character sections (three concurrently) +and merges the notes. Any failed section fails the task; partial results are not +presented as complete. Errors retain their code and message. ## Source map -| File | Responsibility | -| ---------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- | -| `background.js` | `BackgroundService`: registers listeners synchronously, routes messages, runs AI tasks, and handles menus, commands, and cleanup | -| `providers/ai-client.js` | `AIClient` and `PROVIDERS`: provider-specific HTTPS requests, response parsing, retries, and timeouts | -| `core/tasks.js` | Prompt construction, content fencing, chunking, and response parsing | -| `core/document.js` | Shared 2,000,000-character limit and URL/content SHA-256 identity | -| `core/retrieval.js` | Local BM25 passage indexing, ranking, and source excerpts | -| `core/configuration-manager.js` | Settings defaults and persistence; constructs a client for the selected provider | -| `content.js` | Reads page text, metadata, links, images, and selection without modifying the page | -| `scripts/popup-main.js` | Popup/workspace controls, text import, scoped restoration, and escaped result rendering | -| `options.js` | Provider selection, keys, model overrides, appearance, and feature settings | -| `services/storage-service.js` | Local summary and conversation history | -| `services/notification-manager.js` | Desktop notifications for background actions | - -The selected provider is explicit. `ConfigurationManager.createAIClient()` either -constructs that provider's `AIClient` or reports a missing key. Default models and -wire formats live in `PROVIDERS`; there is no provider scoring or automatic -fallback. See [the provider client](../providers/ai-client.js) before adding an API. +| Path | Responsibility | +| ------------------------------- | ------------------------------------------------------------------------------------ | +| `background.js` | Synchronously registered listeners, message routing, tasks, menus, commands, cleanup | +| `content.js` | Read-only DOM text, metadata, links, images, selection | +| `core/configuration-manager.js` | Settings/defaults and construction of the selected client | +| `core/tasks.js` | Prompts, content fencing, chunking, response parsing | +| `core/document.js` | Shared document limit and URL/text SHA-256 identity | +| `core/retrieval.js` | Local BM25 indexing, ranking, source excerpts | +| `providers/ai-client.js` | Provider wire formats, HTTPS, retries, timeouts | +| `scripts/popup-main.js` | Popup/workspace, imports, restoration, escaped rendering | +| `options.js` | Settings UI | +| `services/` | History persistence and desktop notifications | + +Default models and wire formats live in `PROVIDERS`. Selection is explicit: +a missing key is an error, with no scoring or automatic provider fallback. ## Message contract Extension pages send `{ actionType, requestId, payload }` through -`chrome.runtime.sendMessage`. `requestId` is optional and echoed in the response. -`validateMessage()` requires a non-empty action string and this extension's sender -ID. The router rejects unsupported actions. - -| Action | Input / result | -| ----------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | -| `GENERATE_CONTENT_SUMMARY` | `content`, `summaryType`, `targetLength`; returns summary and coverage information | -| `ANSWER_CONTEXTUAL_QUESTION` | `question`, `context`, optional `conversationHistory`, `url`, `title`; returns answer, sources, and retrieval coverage | -| `TRANSLATE_CONTENT` | `text`, `targetLanguage`, optional `sourceLanguage` | -| `ANALYZE_SENTIMENT` | `text`; returns sentiment and reason | -| `EXTRACT_KEY_INSIGHTS`, `GENERATE_SMART_TAGS`, `EXTRACT_ENTITIES` | `text`; returns the requested analysis | -| `EXTRACT_PAGE_CONTENT` | `tabId`, falling back to the sender tab; returns content-script extraction | -| `GET_USER_PREFERENCES`, `UPDATE_USER_PREFERENCES` | Read settings or merge a settings payload | -| `GET_PROVIDER_STATUS` | Configured status for the selected provider and each provider key | -| `TEST_PROVIDER_CONNECTION` | Entered `provider`, `apiKey`, optional `model`; one 16-token request, 10-second timeout, zero retries; returns model, never saves key | -| `GET_DOCUMENT_HISTORY` | `url`, `content`; returns one summary, up to 30 exchanges for that identity, and `memoryEnabled` | -| `CLEAR_DOCUMENT_CHAT` | `url`, `content`; removes only matching exchanges; returns count | -| `GET_HISTORY` | Saved summaries and conversations | -| `EXPORT_USER_DATA` | Schema-v2 object containing settings without API keys and saved history | - -Success responses contain `{ success: true, data, requestId, processingTime }`. -Failures contain `{ success: false, error, errorCode, requestId, processingTime }`. -The popup passes an explicit tab ID because popup messages have no `sender.tab`. -The content script uses its separate `{ action: 'extractContent' }` protocol. +`chrome.runtime.sendMessage`. The optional request ID is echoed. +[Validation](../utils/validation-service.js) requires an object, a nonempty action, +and this extension's sender ID; the [router](../background.js) rejects unknown actions. + +| Action | Input / result | +| ----------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- | +| `GENERATE_CONTENT_SUMMARY` | `content`, `summaryType`, `targetLength`; summary and coverage | +| `ANSWER_CONTEXTUAL_QUESTION` | `question`, `context`, optional `conversationHistory`, `url`, `title`; answer, sources, coverage | +| `TRANSLATE_CONTENT` | `text`, `targetLanguage`, optional `sourceLanguage` | +| `ANALYZE_SENTIMENT` | `text`; sentiment and reason | +| `EXTRACT_KEY_INSIGHTS`, `GENERATE_SMART_TAGS`, `EXTRACT_ENTITIES` | `text`; requested analysis | +| `EXTRACT_PAGE_CONTENT` | `tabId`, falling back to sender tab; extraction | +| `GET_USER_PREFERENCES`, `UPDATE_USER_PREFERENCES` | Read settings or merge a settings payload | +| `GET_PROVIDER_STATUS` | Selected provider and configured-key status | +| `TEST_PROVIDER_CONNECTION` | Entered `provider`, `apiKey`, optional `model`; 16-token request, 10s timeout, no retries; never saves key | +| `GET_DOCUMENT_HISTORY` | `url`, `content`; summary, up to 30 exchanges, `memoryEnabled` | +| `CLEAR_DOCUMENT_CHAT` | `url`, `content`; removes matching exchanges, returns count | +| `GET_HISTORY` | Saved summaries and conversations | +| `EXPORT_USER_DATA` | Schema-v2 settings/history object, excluding API keys | + +Success: `{ success: true, data, requestId, processingTime }`. +Failure: `{ success: false, error, errorCode, requestId, processingTime }`. +Popup messages have no `sender.tab`, so send an explicit tab ID. +The content script separately accepts `{ action: 'extractContent' }`. ## Storage and security -Settings live in `chrome.storage.sync.user_preferences`, including `apiKeys`, -`models`, `preferredProvider`, summary preferences, source/target languages, theme, -and feature flags. -API keys are plaintext at the storage API level and sync with the browser profile. -They are sent only to the selected provider in HTTPS headers and excluded from -exports. See [SECURITY.md](SECURITY.md) for the trust boundaries. - -History lives in `chrome.storage.local` under `genai_summary_history` (up to 1,000 -entries) and `genai_conversation_history` (up to 500). A daily alarm removes entries -older than 90 days; entries without timestamps are retained. Settings have one -reader/writer, `ConfigurationManager`; history belongs to `StorageService`. +`ConfigurationManager` owns `chrome.storage.sync.user_preferences`: +keys, models, provider, summary/language/theme preferences, and feature flags. +Keys are plaintext at the API level, sync with the browser profile, and are +excluded from exports. Unreadable preferences cannot authorize optional effects +or replacement updates. See [security and data handling](SECURITY.md). -Model output is escaped before the popup's small Markdown subset is applied. -Page text is fenced as untrusted data. Questions are limited to 1,000 characters; -single-request tasks use at most 24,000 context characters. Summaries can process -up to eight sections with three section requests in flight, then merge their notes. -There is no telemetry backend or production bundle. +`StorageService` owns local `genai_summary_history` (1,000 entries) and +`genai_conversation_history` (500). A daily alarm prunes entries older than +90 days; entries without timestamps remain. Mutations serialize per key in the +worker, including clear-chat and cleanup. A failed mutation read propagates +instead of overwriting history with an empty list; read-only access retains its +logged fallback. ### Document retrieval, cache, and memory -The worker hashes URL plus trimmed text to identify a document. A changed page or -different URL starts a new thread. Older unscoped history remains exportable but -is not restored into a page. Full text imports and indexes are not persisted. +Identity hashes URL plus trimmed text. Changed text/URLs start another thread; +old unscoped records remain exportable without being restored. Full imports and +indexes are not persisted. -```mermaid -flowchart LR - doc["Page or text import ≤2M characters"] --> id["URL/content identity"] - id --> index["BM25 index, up to 3 cached documents"] - question["Question + previous user wording"] --> index - index --> passages["Up to 6 passages, source labels"] - passages --> prompt["Fenced question prompt"] - prompt --> api["Selected provider"] - api --> saved["Answer + focused source excerpts in local history"] -``` +Retrieval selects up to six 2,800-character passages with 200-character overlap. +It uses question and prior user wording; no-match queries sample across the +document. This is lexical retrieval, with no guarantee of semantic or exhaustive +recall. The three-document cache evicts the oldest insertion and disappears on +worker suspension. Restoration returns at most 30 exchanges; prompts use bounded +recent conversation. See [retrieval research](product/RESEARCH.md). + +Summary keys include `summary-v1`, document identity, provider, model, style, +and length. Bump the version when summary prompt meaning changes. Save history +off disables saves, cache reuse, and restoration; it does not delete records. +The worker checks the preference again before saving an in-flight result. -Passages are at most 2,800 characters with 200-character overlap. Retrieval is -lexical; no-match queries sample across the document. It does not guarantee -semantic recall or whole-book coverage. The index cache evicts the oldest inserted -document at three entries and is lost on worker suspension. The next question -rebuilds it. Restored exchanges use at most 30 turns; prompts use the bounded -recent conversation in `core/tasks.js`. - -Summary cache keys include `summary-v1`, identity, provider, model, style, and -length. Bump the cache version if summary prompt meaning changes. The request -lifecycle above is skipped on a matching saved result. Save-history off disables -new saves, summary cache reuse, and restoration; it does not erase past records. -The preference is read again before persisting an in-flight result. - -Storage read-modify-write operations are serialized per history key in the single -worker. Clear-chat and daily cleanup use the same queues. A failed mutation read -propagates rather than overwriting history with an empty list. Read-only history -access retains its logged fallback behavior. -Generated summaries are stored once as `summary`; `text` is reconstructed for the -response contract. If a history write fails (including storage quota), summary -and chat results still reach the UI with a visible warning to copy the result. -No second AI request is made just to retry persistence. - -`popup.html?workspace=1&tabId=123` uses an explicit source tab; ordinary popup -startup queries the active tab. Workspace text/Markdown import validates an 8 MB -file limit and the shared character limit. Import and extraction trigger no AI -request. Imported material uses a `local-document:` filename identity plus its -content hash. File content is read only after the user chooses it. +Summaries are stored once as `summary`; response `text` is reconstructed. +History/quota failures leave generated summary/chat output visible with a save +warning, without another provider request. + +`popup.html?workspace=1&tabId=123` binds a workspace to its source tab; ordinary +popup startup queries the active tab. User-selected UTF-8 text/Markdown imports +validate 8 MB and 2,000,000-character limits. Their identity uses +`local-document:`, filename, and content hash. Extraction/import sends no AI request. ## Local validation -Use Node >=20.19 and npm >=10, as declared in [package.json](../package.json). +Use Node >=20.19 and npm >=10. ```bash npm ci @@ -204,49 +126,47 @@ npm audit --audit-level=high npm run benchmark:retrieval ``` -Vitest imports the shipped controllers and mocks `chrome.*` and provider fetches. -Coverage includes all shipped JavaScript; the global branch threshold is 78%, -with a separate 75% floor for `background.js`. Other thresholds are in -[vitest.config.js](../vitest.config.js). +`verify` runs lint, typecheck, and Vitest. Tests import shipped controllers and +mock Chrome APIs/provider fetches. [Coverage floors](../vitest.config.js) cover +all shipped JavaScript: statements/functions 85%, lines 88%, branches 78%, and +worker branches 75%. -Playwright loads the actual extension in persistent Chromium contexts. Fixture -pages and provider responses are intercepted. A real toolbar-icon click granting -`activeTab` remains a manual release check; intercepted tests do not demonstrate -live provider credentials, model availability, or output quality. +Playwright uses the actual unpacked extension in persistent Chromium contexts, +with intercepted fixture pages/provider responses. A real toolbar click granting +`activeTab` remains a manual release check. These tests do not verify live keys, +model access/quality, native OS notices, or minimum-browser compatibility. ## Pull requests and packaging -CI runs lint, typechecking, and coverage on Node 20/22/24; Node 22 also runs the -dependency audit. Browser tests run before the package job. - -The workflow selects `ubuntu-26.04` explicitly and pins action release commits. -Action runtimes are separate from the Node versions tested by the project. -Checkout credentials are not retained after checkout; the ancestry guard uses -the full fetched history. npm caching uses `package-lock.json` explicitly, and +[CI](../.github/workflows/ci.yml) runs lint, types, and coverage on Node 20/22/24; +Node 22 also audits dependencies. Browser tests precede packaging. +Ubuntu 26.04 and action release SHAs are explicit. Action execution runtimes are +separate from the application's Node matrix. Checkouts do not persist credentials; +the ancestry guard fetches full history. Install jobs cache npm using the lockfile; the dependency-free guard disables automatic package-manager caching. -See [migration decisions and validation](ci-modernization-evidence.md) before -updating these pins. Codecov remains optional; a green job alone does not prove -that coverage reached the service. - -The PR-base job rejects a non-default base branch already contained in the default -branch. Retarget the PR at the default branch when this fails. The check runs on -opening, reopening, head updates, and edits (including retargeting). It checks Git -ancestry at workflow time; squash/rebase equivalence and future base merges are -not continuously monitored. Tests in `tests/ci/pr-base.test.js` exercise the guard -against temporary Git histories. - -The repository root is the loadable extension. Load it unpacked at -`chrome://extensions`, then reload after source edits. Legacy `dist/` and `build/` -output are ignored by Git and ESLint and can be left in an older checkout. - -The CI ZIP explicitly includes `manifest.json`, the runtime root JavaScript and -HTML/CSS files, `assets/`, `core/`, `providers/`, `scripts/`, `services/`, `styles/`, -`utils/`, `README.md`, and `LICENSE`. Keep that list in -[ci.yml](../.github/workflows/ci.yml) current when adding runtime files. CI uploads -the ZIP as an Actions artifact; Chrome Web Store publishing is a separate release -step. Install and configure the unpacked extension using [README.md](../README.md). - -For consolidation decisions and verification evidence, see -[INTEGRATION-REVIEW.md](INTEGRATION-REVIEW.md). -For product scope, research, and retrieval measurements, see -[product/PRODUCT.md](product/PRODUCT.md) and [product/RESEARCH.md](product/RESEARCH.md). +Codecov is optional: green status alone does not establish upload success. +See [CI migration evidence](ci-modernization-evidence.md) before changing pins. + +The PR-base guard rejects a non-default base already contained in the default +branch. Retarget after its base lands. It runs on opening, reopening, head updates, +and edits; it checks workflow-time ancestry, not squash/rebase equivalence or +future merges. [Tests](../tests/ci/pr-base.test.js) use temporary Git histories. +The guard is independent of the verify job; required-check policy is a separate +repository setting. + +Load the repository root at `chrome://extensions` and reload after edits. +Legacy ignored output is not a runtime input. +The CI ZIP allowlist contains the manifest, root runtime JS/HTML/CSS, assets, +runtime module directories, README, and LICENSE. +The npm `files` list in [package.json](../package.json) covers the same runtime; +npm also includes package metadata. Check both lists when adding runtime files: + +```bash +npm pack --dry-run --json --ignore-scripts +``` + +Verify manifest targets, relative imports, and HTML assets in the resulting +package. CI uploads an Actions artifact; publication/store release is separate. +See [contribution release steps](../CONTRIBUTING.md#release-process), +[historical integration evidence](INTEGRATION-REVIEW.md), and the +[product brief](product/PRODUCT.md). diff --git a/docs/INTEGRATION-REVIEW.md b/docs/INTEGRATION-REVIEW.md index e1ae361..528131e 100644 --- a/docs/INTEGRATION-REVIEW.md +++ b/docs/INTEGRATION-REVIEW.md @@ -1,124 +1,61 @@ # Integration review -Reviewed 17 October 2026 against `main` at +Historical review: 17 October 2026, base [`b915866`](https://github.com/aaron-seq/GenAI-Browser-Tool/commit/b91586665941d3456f7411557edabc51842892d9). +The [full original record](https://github.com/aaron-seq/GenAI-Browser-Tool/blob/6e8288550ef4935684a6a44c87cbbed77b818d14/docs/INTEGRATION-REVIEW.md) +preserves the detailed inventory and run notes; results below are historical, +not tests rerun by each reader. ## Decision and scope -No PRs were open when this review began. [PR #24](https://github.com/aaron-seq/GenAI-Browser-Tool/pull/24) -had already recovered PRs #14 and #15 into `main`. Start the integration branch -from current `main`, preserve that implementation, and fix validation findings in -one PR targeting `main`. +[PR #24](https://github.com/aaron-seq/GenAI-Browser-Tool/pull/24) had recovered +PRs #14/#15; no PR was open when the review began. Integrate from current main, +preserve the source-loaded MV3 runtime, and fix demonstrated validation problems. -Older refs were inspected and preserved. The old `feat/close-open-issues` branch -contains an alternative test suite superseded by the controllers and coverage -work in 5.2.0. The January `fix/critical-stub-implementations-and-provider-interface` -branch adds an unused abstract provider class for the removed provider system. -Neither represents an open PR or a reason to restore that architecture. - -## Engineering review before implementation - -| Area | Finding / decision | -| ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Known facts | Latest `main` contains the recovered functionality; the local checkout was behind. Baseline typecheck and 219 tests passed. | -| Unknown facts | Live credential validity, provider model availability, and real toolbar permission behavior were not demonstrated. | -| Assumptions | `main`, confirmed as the repository default, is the requested integration destination. Preserve existing extension behavior. | -| Clarification questions | None required for this integration; merge authorization was explicit. | -| Success criteria | Lint, types, coverage, browser tests, audit, package inspection, and PR CI pass before merging. | -| Root cause | Earlier stacked work targeted a base already merged; CI validated code without checking whether its destination still needed integration. | -| Architecture | Keep the source-loaded MV3 extension and one selected-provider client. No build system or runtime dependencies added. | -| Security | Patch vulnerable development dependencies; use read-only CI permissions; keep branch names out of shell interpolation; package explicit runtime paths. | -| Performance / scalability | Keep request concurrency, chunk limits, history caps, and retry behavior. No runtime performance changes or new services. | -| Maintainability | Ignore old generated files, document the real interfaces, and test worker lifecycle behavior through registered listeners. | -| Testing strategy | Real temporary Git histories for the base guard, existing mocked worker integration tests, coverage floors, and persistent Chromium E2E. | -| Dependencies | Compatible transitive updates only; no major upgrades, new dependencies, or changes to the shipped extension. | -| Alternatives | Re-merging superseded branches adds dead code; a validation-only report leaves demonstrated audit and packaging problems unresolved. | -| Recommendation | Apply the focused fixes and merge one tested integration PR. | -| Risk | CI ancestry checks observe workflow-time Git history, not patch equivalence or future events; the explicit ZIP input list needs updating for new runtime paths. | -| Rollback | Revert the integration merge on `main` and rerun CI. Existing settings/history and old branches are preserved. | - -Five whys: changes missed `main` because later PRs landed on an old feature branch; -that branch remained a target after its parent PR merged; stacking was valid when -work began; checks verified code but not the target's ancestry; there was no -automated destination check. [Issue #22](https://github.com/aaron-seq/GenAI-Browser-Tool/issues/22) -records the history and acceptance criteria. - -The guard uses [Git's documented `--is-ancestor` status codes](https://git-scm.com/docs/git-merge-base), -[full-history checkout](https://github.com/actions/checkout#fetch-all-history-for-all-tags-and-branches), -and [GitHub's read-only workflow permissions](https://docs.github.com/en/actions/reference/workflows-and-actions/workflow-syntax#permissions). -It runs on PR opening, reopening, head updates, and edits. It does not continuously -monitor future base merges or detect squash/rebase equivalence. Branch protection -was absent at inspection; this PR adds a check without changing repository policy. +Preserve older refs without restoring their superseded test suite or unused +abstract provider implementation. The delivery problem was a stacked base already +merged into main, not missing runtime functionality. +[Issue #22](https://github.com/aaron-seq/GenAI-Browser-Tool/issues/22) records the +ancestry guard; [#23](https://github.com/aaron-seq/GenAI-Browser-Tool/issues/23) +records worker regression coverage. Current behavior and guard limits belong in +[DEVELOPMENT.md](DEVELOPMENT.md#pull-requests-and-packaging). ## Validation personally run -Windows, Node 22.18.0, npm 11.6.2. Provider HTTP calls were mocked/intercepted. - -| Check | Baseline | Integration result | -| ------------------------- | ---------------------------------------------- | -------------------------------------------------- | -| Dependency installation | `npm ci` succeeded | Compatible lockfile updates installed successfully | -| Lint | Failed on pre-existing generated `dist/` files | Passed with old output preserved and ignored | -| Typecheck | Passed | Passed | -| Unit + integration tests | 219 passed, 11 files | 241 passed, 12 files | -| Statement / line coverage | 91.28% / 93.88% | 92.80% / 95.59% | -| Global branch coverage | 77.00%, floor 72% | 80.47%, floor 78% | -| Worker branch coverage | 57.14% | 83.92%, separate floor 75% | -| Worker function coverage | 91.66% | 100% | -| Chromium E2E | Earlier published results are in PR #24 | 13 passed on this branch | -| `npm audit` | 3 high-severity vulnerable packages | 0 vulnerabilities | - -The local extension ZIP contained 22 runtime/documentation files. All manifest -targets and 16 relative JavaScript/HTML references were present; tooling and -legacy output were excluded. GitHub's ZIP will also be inspected before merge. +The original session ran on Windows / Node 22.18.0 / npm 11.6.2, with +mocked/intercepted provider requests. -The 22 additional cases cover merged/equal/open/missing Git bases and a renamed -default branch; context-menu page extraction, absent input and notification size; -commands with/without an active tab; install/update, startup failure, cleanup and -unrelated alarms; and unsuccessful extraction and sender-tab fallback. Sources: -[guard tests](../tests/ci/pr-base.test.js), -[worker tests](../tests/integration/extension-workflow.test.js), and -[coverage configuration](../vitest.config.js). These address -[issue #23](https://github.com/aaron-seq/GenAI-Browser-Tool/issues/23). +| Check | Before | Integration result | +| ------------------------ | ------------------------------------------- | ----------------------------------------------- | +| Lint / types | Lint failed on legacy `dist/`; types passed | Both passed; legacy output retained and ignored | +| Unit/integration | 219 tests / 11 files | 241 / 12 | +| Statements / lines | 91.28% / 93.88% | 92.80% / 95.59% | +| Global / worker branches | 77.00% / 57.14% | 80.47% / 83.92%; floors 78% / 75% | +| Chromium E2E | Earlier evidence on PR #24 | 13 passed, no retries | +| npm audit | Three high-severity vulnerable packages | Zero vulnerabilities reported | -The first Chromium launch was cold; all 13 cases passed without retries. The -runner emitted an environment warning because both `NO_COLOR` and `FORCE_COLOR` -were set. This affects terminal coloring, not browser behavior. +The local ZIP had 22 files and all manifest targets/16 relative runtime references; +tooling and legacy output were excluded. This is the historical package, not the +current release count. Added tests used real temporary Git histories and worker +listeners for menu/command/lifecycle/alarm/extraction branches. +Run current checks from [local validation](DEVELOPMENT.md#local-validation). ## Dependency decisions -All changes remain within existing dependency ranges, add no runtime cost or -bundle content, retain upstream licenses, and require no application migration. -Rollback restores the prior lockfile, including its known vulnerabilities. - -| Dependency | Before → after | Evidence | -| ----------------- | --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | -| `brace-expansion` | 5.0.9 → 5.0.12 | [Expansion DoS advisory](https://github.com/advisories/GHSA-q2hr-2g5m-vwhr), [recursion advisory](https://github.com/advisories/GHSA-qhr7-859c-m2p7) | -| `source-map-js` | 1.2.1 → 1.2.2 | [Indexed source-map DoS advisory](https://github.com/advisories/GHSA-68fv-2mgg-jv7q) | -| `undici` | 7.29.0 → 7.30.1 | [TLS validation advisory](https://github.com/advisories/GHSA-w293-vg96-wgc3); the fresh npm audit also reported retry/WebSocket/cache advisories | - -## Changed files and operational notes - -| Files | Reason | -| ---------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- | -| `.github/workflows/ci.yml`, `.github/scripts/check-pr-base.mjs` | Base ancestry guard, read-only permissions, dependency audit, explicit ZIP inputs | -| `.gitignore`, `eslint.config.js` | Preserve and ignore legacy output; recognize the Node-based CI script | -| `package-lock.json` | Three security updates and root-version alignment with existing package/manifest version 5.2.0 | -| `tests/ci/pr-base.test.js`, `tests/integration/extension-workflow.test.js`, `vitest.config.js` | Regression cases and higher enforced branch floors | -| `CONTRIBUTING.md`, `docs/DEVELOPMENT.md`, `CHANGELOG.md`, this review | Accurate prerequisites, actual runtime contracts, integration and validation evidence | - -Run the commands in [the development guide](DEVELOPMENT.md#local-validation). -The ZIP packages runtime inputs explicitly; it must contain every manifest, -relative import, and HTML asset target. CI produces an Actions artifact, not a -published store release. No data migration is required. - -No performance benchmark, load/stress test, accessibility audit, Edge test, live -provider evaluation, or real toolbar-click smoke test was performed. Passing the -available automated checks does not establish those outcomes. - -Remaining feature work stays in existing issues: -[#16](https://github.com/aaron-seq/GenAI-Browser-Tool/issues/16) provider key verification, -[#17](https://github.com/aaron-seq/GenAI-Browser-Tool/issues/17) long-page task coverage, -[#18](https://github.com/aaron-seq/GenAI-Browser-Tool/issues/18) translation preferences, -[#19](https://github.com/aaron-seq/GenAI-Browser-Tool/issues/19) import, -[#20](https://github.com/aaron-seq/GenAI-Browser-Tool/issues/20) repository formatting, -and [#21](https://github.com/aaron-seq/GenAI-Browser-Tool/issues/21) persistent chat UI. +Compatible transitive updates retained licenses, added no runtime dependency, +and needed no application migration: + +- `brace-expansion` 5.0.9 → 5.0.12: + [expansion DoS](https://github.com/advisories/GHSA-q2hr-2g5m-vwhr) and + [recursion advisory](https://github.com/advisories/GHSA-qhr7-859c-m2p7). +- `source-map-js` 1.2.1 → 1.2.2: + [indexed source-map DoS](https://github.com/advisories/GHSA-68fv-2mgg-jv7q). +- `undici` 7.29.0 → 7.30.1: + [TLS validation advisory](https://github.com/advisories/GHSA-w293-vg96-wgc3); + npm also reported retry/WebSocket/cache advisories. + +Reverting restores the prior workflow/lockfile, including known vulnerabilities. +No benchmark, load test, accessibility audit, Edge/live-provider test, or real +toolbar-click check was performed. CI artifacts are not store publication. +Remaining product work is maintained in the [product brief](product/PRODUCT.md) +and [current checkpoint](product/PROJECT-STATUS.md). diff --git a/docs/SECURITY.md b/docs/SECURITY.md index e477101..dbf9fa3 100644 --- a/docs/SECURITY.md +++ b/docs/SECURITY.md @@ -2,299 +2,142 @@ ## Supported Versions -We actively support the following versions with security updates: +| Version | Supported with security updates | +| ------- | ------------------------------- | +| 5.3.x | Yes | +| 5.2.x | Yes | +| 5.1.x | Yes | +| <5.1 | No | -| Version | Supported | -| ------- | ------------------ | -| 5.3.x | :white_check_mark: | -| 5.2.x | :white_check_mark: | -| 5.1.x | :white_check_mark: | -| < 5.1 | :x: | - -Versions before 5.0 shipped stub AI providers and are not a supported -configuration. +Versions before 5.0 shipped stub AI providers and are not supported. ## Reporting a Vulnerability ### How to Report -If you discover a security vulnerability in GenAI Browser Tool, please report it responsibly: - -1. **DO NOT** create a public GitHub issue -2. **DO NOT** disclose the vulnerability publicly until it has been addressed -3. **DO** email security concerns directly to: aaronsequeira12@gmail.com -4. **DO** provide detailed information about the vulnerability +Email **aaronsequeira12@gmail.com**, subject +`[SECURITY] GenAI Browser Tool - [Brief Description]`. +Do not file a public issue or disclose the vulnerability before it is addressed. ### What to Include -When reporting a security vulnerability, please include: - -- **Description**: Clear description of the vulnerability -- **Impact**: Potential impact and severity assessment -- **Reproduction**: Step-by-step instructions to reproduce -- **Evidence**: Screenshots, logs, or proof of concept (if applicable) -- **Environment**: Browser version, extension version, operating system -- **Contact**: Your preferred method of communication for follow-up +Send the impact, reproduction steps, redacted evidence, browser/extension/OS +versions, and a way to follow up. Do not send keys or unrelated private page data. ### Response Timeline -- **Acknowledgment**: Within 48 hours of report -- **Initial Assessment**: Within 5 business days -- **Status Updates**: Weekly until resolution -- **Resolution**: Target 30 days for critical issues, 90 days for others +- Acknowledgment: within 48 hours. +- Initial assessment: within five business days. +- Status updates: weekly until resolution. +- Resolution targets: 30 days for critical issues, 90 days for others. ## Security Measures ### Content Security Policy -```json -{ - "content_security_policy": { - "extension_pages": "script-src 'self'; object-src 'self'" - } -} -``` - -Extension pages load no remote scripts and no remote stylesheets. Network access -is limited to the three provider hosts declared in `host_permissions`. +The [manifest](../manifest.json) sets +`script-src 'self'; object-src 'self'`. Extension pages load no remote scripts +or stylesheets. Provider hosts are explicitly declared in `host_permissions`. ### Input validation -- **Message validation** (`utils/validation-service.js`): every - `chrome.runtime` message must be an object carrying a non-empty string - `actionType` and a sender whose id is this extension's own, or it is rejected - before any work is done. An `actionType` that is well formed but unrecognised - is rejected by the router's `default` case, still before any work — the list - of valid actions lives in exactly one place rather than being mirrored here. -- **Input limits**: page/text documents are limited to 2,000,000 characters. - Chat selects at most six passages of 2,800 characters. Translation and other - single-request analyses clip context to 24,000 characters. Summaries can send - up to eight sections of 24,000 characters, followed by a merge request. -- **Question length cap**: 1,000 characters. This bounds token spend; it is not - a security boundary. - - There is deliberately **no content filter on questions**. An earlier version - rejected questions matching patterns like `/