Skip to content
This repository was archived by the owner on Jun 18, 2026. It is now read-only.

Latest commit

 

History

History
315 lines (242 loc) · 14.8 KB

File metadata and controls

315 lines (242 loc) · 14.8 KB

Contributing to Agentic Chat

Thanks for your interest in contributing! This guide will help you get started.

Table of Contents

Code of Conduct

Be respectful, constructive, and inclusive. We're all here to build something useful.

Getting Started

  1. Fork the repository on GitHub
  2. Clone your fork locally:
    git clone https://github.com/<your-username>/agenticchat.git
    cd agenticchat
  3. Create a branch for your work:
    git checkout -b feature/your-feature-name

Development Setup

Agentic Chat is a zero-dependency browser application. The core app (index.html, app.js, style.css) requires no build tools — just open index.html in a browser.

For running tests:

npm install          # Install dev dependencies (Jest)
npm test             # Run the test suite

Requirements

  • Node.js ≥ 18.0.0 (for tests only)
  • A modern browser (Chrome, Firefox, Safari, Edge)
  • An OpenAI API key with GPT-4o access (for manual testing)

Architecture Overview

The codebase is a single-file application (app.js, ~60k LOC) composed of many self-contained IIFE modules. For a full architectural deep-dive see ARCHITECTURE.md. The core modules are:

Module Responsibility
ChatConfig Constants and configuration (model, limits, prompts)
ConversationManager Chat history management, trimming, token estimation
SandboxRunner iframe sandbox creation, code execution, cancellation
ApiKeyManager OpenAI key storage, per-service key prompts, modal handling
UIController All DOM updates, button state, character counter
ChatController Orchestrates message flow, API calls, response processing
OpenAIClient Streaming SSE fetch wrapper, error mapping
SafeStorage / sanitizeStorageObject Trust boundary for localStorage (prototype-pollution defence)
_escapeHtml Canonical HTML escape used everywhere strings hit innerHTML
ChatOutputObserver Shared MutationObserver for #chat-output — register a callback instead of spawning your own observer
TextAnalysisUtils Shared tokenization, stop-words, jaccard, cosine, TF helpers
Agentic siblings ReplyStyleAdvisor, AssistantConfidenceCalibrator, ConversationHealthMonitor, PromptClarityCoach, PrivacyLeakDetector, ToolCallSafetyAdvisor, OpenLoopTracker, ContextWindowOptimizer, … (search app.js for Add / feat(app): history)

All modules communicate through public APIs. DOM manipulation is isolated to UIController except where the sandbox requires direct access.

Trust boundaries (read this before touching parsing or rendering)

Three primitives form the security perimeter — regressions here become app-wide XSS or prototype pollution:

  • _escapeHtml(str) — escapes & < > " ' in one pass. Use it for every value interpolated into innerHTML / template literals that produce HTML. Local esc() helpers in submodules must escape the same five characters (single-quote included) so they remain safe in both "..." and '...' attribute contexts.
  • sanitizeStorageObject(obj) — strips __proto__ / constructor / prototype keys recursively. Every JSON.parse from localStorage or imported conversations must go through it (or via _safeParse).
  • _safeParse(raw, fallback) — one-shot JSON.parse + sanitize + fallback. Prefer this over raw JSON.parse for any persisted or imported data.

The focused regression suite is tests/security-primitives.test.js. Add cases there when you touch any of the three.

Key Files

├── index.html              # Entry point — single HTML file
├── app.js                  # All application logic (~60k LOC, modular IIFEs)
├── sw.js                   # Service worker (offline cache + navigation fallback)
├── style.css               # Styles
├── manifest.json           # PWA manifest
├── ARCHITECTURE.md         # Deep-dive architecture notes
├── tests/                  # 90+ focused Jest suites (one per module)
│   ├── setup.js            # Shared jsdom + global stubs (always loaded)
│   ├── security-primitives.test.js  # _escapeHtml / sanitizeStorageObject / _safeParse
│   ├── app.test.js         # Core ChatController / ConversationManager coverage
│   └── <module>.test.js    # One file per agentic / utility module
├── scripts/
│   └── stamp-sw.js         # Builds versioned CACHE_NAME into sw.js
├── docs/                   # GitHub Pages documentation site
├── codecov.yml             # Codecov thresholds
├── .c8rc.json              # c8 coverage config
├── jest.config.js          # Jest config (jsdom env, setupFiles → tests/setup.js)
├── Dockerfile              # Container build (nginx-alpine, SBOM + provenance)
└── .github/
    ├── copilot-setup-steps.yml   # Bootstraps Copilot coding agents
    ├── copilot-instructions.md   # Repo-specific guidance for AI agents
    └── workflows/                # CI, CodeQL, Docker, Pages, npm publish, labeler, stale

Making Changes

Before You Start

  • Check existing issues to avoid duplicate work
  • For large changes, open an issue first to discuss the approach
  • Keep changes focused — one feature or fix per PR

Guidelines

  • Read the code first. Understand the module structure before modifying.
  • No new runtime dependencies. The app is intentionally zero-dependency in the browser. Dev dependencies (testing) are fine.
  • Security matters. Sandbox isolation, CSP headers, and nonce validation are critical. Don't weaken them.
  • Keep it simple. This is a lightweight tool, not a framework.

Testing

Tests use Jest with jsdom environment:

npm test                            # Run all tests (jest --verbose)
npx jest --watch                    # Watch mode during development
npm run test:coverage               # c8 + jest, full coverage report (text + html + lcov)
npm run test:coverage:ci            # Same, but lean reporters for CI
npm run coverage:open               # Open the generated coverage/index.html
npx jest tests/app.test.js          # Run specific test file
npx jest -t 'escapes the five HTML' # Run a single named test

💡 Heads-up on the full suite. npm test walks all 90+ suites and exercises a lot of jsdom code paths; on cold machines it can take several minutes. While developing, scope to the suite(s) you're touching (npx jest tests/<file>.test.js) and only run the full suite before pushing. CI runs the full suite for you.

Writing Tests

  • Place test files in tests/ with .test.js extension
  • Use tests/setup.js for shared DOM mocks and fixtures
  • Test behavior, not implementation details
  • Cover edge cases and error paths, not just happy paths

Example:

describe('ConversationManager', () => {
  test('should trim history when exceeding max pairs', () => {
    // Arrange: fill history beyond MAX_HISTORY_PAIRS
    // Act: add one more message
    // Assert: oldest messages were removed
  });
});

Pull Request Process

  1. Ensure tests pass: npm test must succeed locally (or at minimum, the suites your change touches; CI runs the full matrix). New behaviour needs a new test in the matching tests/<module>.test.js — don't pile assertions into app.test.js.
  2. Write descriptive commits: Use clear, imperative commit messages
    • Fix sandbox timeout not clearing on cancel
    • Add input validation for API key format
    • Fixed stuff
    • Update app.js
  3. Update documentation if your change affects usage or behavior
  4. Keep PRs small — easier to review, faster to merge
  5. Fill in the PR template (if present) with context on what and why

Review Criteria

  • Does it maintain security guarantees (sandbox isolation, CSP, nonce validation)?
  • Are there tests for new behavior?
  • Does it follow the existing code style?
  • Is the change necessary and well-scoped?

Commit Convention

This project follows Conventional Commits for clear, machine-readable history:

<type>(<scope>): <description>
Type When to Use
feat New user-facing feature
fix Bug fix
perf Performance improvement
refactor Code restructuring without behavior change
test Adding or updating tests
docs Documentation only
ci CI/CD pipeline changes
chore Tooling, config, housekeeping
security Security fix or hardening

Common scopes (optional, lowercase, matching what the git log already uses): app, sandbox, api, ui, config, tests, sw (service worker), docs, deps, docker, shortcuts, pages, ci, codeql, labeler, plus the name of a specific agentic module when the change is module-local (e.g. replystyleadvisor, deadline-tracker).

Examples:

feat(sandbox): add execution timeout configuration
fix(ui): prevent double-submit on rapid Enter key
perf(api): batch token estimation for multi-turn history
security(sandbox): strengthen CSP nonce validation

Style Guide

JavaScript

  • Use 'use strict' (already set globally)
  • Use const by default, let when reassignment is needed, never var
  • Use descriptive names: handleUserInput not hUI
  • Keep functions focused — one responsibility per function
  • Add JSDoc comments for public module APIs
  • Use Object.freeze() for configuration objects
  • Prefer early returns over deeply nested conditionals
  • Use ?? (nullish coalescing) over || when 0 or '' are valid values
  • Avoid magic numbers — define constants in ChatConfig

CSS

  • Use CSS custom properties (variables) for theming values
  • Follow existing naming conventions
  • Mobile-first: ensure changes work on small screens
  • Group properties: positioning → box model → typography → visual → misc

HTML

  • Semantic elements where appropriate
  • Accessible: labels, ARIA attributes, keyboard navigation
  • All interactive elements must be keyboard-reachable (tabindex, focus styles)

Reporting Bugs

Open an issue with:

  1. Summary — One sentence describing the bug
  2. Steps to reproduce — Exact steps to trigger the problem
  3. Expected behavior — What should happen
  4. Actual behavior — What actually happens
  5. Environment — Browser, OS, any relevant extensions
  6. Screenshots — If applicable

Requesting Features

Open an issue with:

  1. Problem statement — What limitation or need does this address?
  2. Proposed solution — How would it work from a user perspective?
  3. Alternatives considered — What else did you think about?
  4. Scope — Is this a small tweak or a significant change?

AI Coding Agents

This repo is configured for GitHub Copilot coding agents (Claude, Codex). If you're assigned an issue or want to experiment:

  • The .github/copilot-setup-steps.yml handles dependency installation and build automatically
  • .github/copilot-instructions.md provides repo-specific architecture context to the agent
  • AI agents can open PRs against issues — review them like any other PR
  • When reviewing AI-generated PRs, pay extra attention to:
    • Sandbox security — any changes to iframe isolation or CSP must be scrutinized
    • Zero-dependency policy — agents sometimes add runtime dependencies
    • Test coverage — ensure new behavior has corresponding tests

Security Vulnerabilities

If you discover a security vulnerability, do not open a public issue. Instead, email the maintainer directly or use GitHub's private vulnerability reporting. Security issues in the sandbox isolation, CSP enforcement, or API key handling are especially critical.

Local Development Tips

  • Quick start: Open index.html directly with file:// protocol — no server needed for basic development.
  • Live reload: Use any static server with watch mode (e.g., npx serve . or VS Code Live Server extension).
  • Debugging sandbox: The iframe sandbox uses srcdoc — inspect it via browser DevTools → Elements → find the <iframe> and switch to its context in the console.
  • API mocking: For testing without burning API credits, mock fetch in your test setup (see tests/setup.js for examples).
  • Service Worker: Changes to sw.js require clearing the SW cache in DevTools → Application → Service Workers → Unregister, then hard refresh.
  • Service Worker cache stamp: When you change index.html, app.js, or style.css, re-run npm run build:sw and commit the updated sw.js. CI runs node scripts/stamp-sw.js --check and will fail the build if the stamp is stale, so users never get served a mismatched cache. You can verify locally any time with the same command (--check reads only, never writes).

Troubleshooting

Problem Solution
Tests fail with ReferenceError: document is not defined Ensure tests/setup.js is loaded — check jest.config or package.json setupFiles
Sandbox code execution hangs Check SandboxRunner timeout config; the iframe srcdoc may have a CSP error — look at browser console
API key modal won't dismiss Clear localStorage key openai_api_key and refresh
Service worker serves stale assets Unregister SW in DevTools → Application → Service Workers, then hard refresh (Ctrl+Shift+R)
npm test passes but browser shows errors jsdom and real browsers differ — test in Chrome DevTools console too
Changes to style.css not visible SW cache — see Service Worker tip above, or use DevTools → Network → Disable cache

Branching Strategy

We use a simple trunk-based flow:

  • main — stable, always deployable
  • Feature branches: feature/<short-name> (e.g., feature/voice-input)
  • Bug fixes: fix/<issue-number>-<short-name> (e.g., fix/42-sandbox-timeout)
  • Keep branches short-lived — merge within a few days, not weeks
  • Rebase onto main before opening a PR to keep history linear

License

By contributing, you agree that your contributions will be licensed under the MIT License.