# Project export: Foresight

This document was generated by HackStack to give an AI agent context about a hackathon project. Sections are labeled with their provenance; content marked as truncated was cut to keep this document small.

## Project metadata

- Hackathon: OpenAI Build Week
- Tagline: Predicts the right GPT-5.6 tier for your task before it runs, then watches your Codex session live and kills it if it goes rogue on cost, scope, or secrets.
- Devpost: https://devpost.com/software/foresight-40gouj
- GitHub: https://github.com/AmanM006/foresight
- Demo: https://pypi.org/project/foresight-agent-guard/
- Video: https://www.youtube.com/embed/ZnMxyWLX2lk?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Team: 1 GitHub contributor(s) — Aman (83 commits)

## Devpost submission (written by the team)

### Inspiration

Honestly this started from me being annoyed. I use Codex a lot for my hackathon builds and lately my sessions felt weirdly unpredictable I'd do something small and suddenly a big chunk of my 5-hour limit was just gone. No warning, no explanation, nothing. Turned out this wasn't just me. I went digging and found an actual GitHub issue (openai/codex#32250) of someone's Pro quota dropping from 87% to 76% during literally one short conversation. Bunch of duplicate issues linked to it too, plus Reddit threads full of people confused about the same thing. Apparently there's also this "Ultra" reasoning mode that spins up a bunch of sub-agents with no real limit on how many, which is a big chunk of why people's usage explodes without warning. So I kept thinking why does nothing catch this before it happens? You just find out after your quota's gone. That's basically the whole reason I built this.

### What it does

You wrap your coding agent with Foresight instead of running it directly. Before anything starts, it asks GPT-5.6 to look at your task and predict how complex it actually is do you need Luna, Terra, or Sol, and at what effort level. So you're not just defaulting to Sol for something that's basically a rename. Then while the agent's actually running, Foresight watches it every file write, every subprocess call, whether it's staying in scope or wandering off into files it shouldn't touch, whether it's trying to read something it shouldn't (like a .env file). If it crosses a line it gets killed on the spot instead of quietly burning through your budget. One thing I want to be upfront about: there are two different modes depending on what you're wrapping, and they're not equally strong. If you're running your own Python script as the agent, Foresight can actually block a bad action before it happens. If you're wrapping something external like the real Codex CLI, it can't reach inside that process, so it watches the filesystem instead and reacts after the write already happened. I didn't want to pretend these work the same way, because they don't. How I built it All of it with Codex, over this week. I didn't plan the whole thing out upfront I started with just intercepting file/subprocess calls in a Python process, got that working, then kept noticing more failure modes worth catching and adding them one at a time. Hard action limits so a task can't just run forever. Detecting when it looks like a bunch of sub-agents are spawning at once. A live "you'll run out of budget in about N minutes at this rate" forecast. At some point I also had Codex build out a benchmark suite so I wasn't just trusting my own demo ran it against 15 hand-labeled tasks (got 14/15 right on tier prediction) and 10 adversarial prompt-injection attempts (caught 9/10). It's a real published PyPI package now too (foresight-agent-guard), and I actually tested it wrapping the live Codex CLI, not a fake version of it. Challenges I ran into The one that actually made me stop and think: I originally built my demo around an MFA/auth task, assuming GPT-5.6 would predict a narrow scope for it. It didn't. It correctly said this touches auth/identity stuff, that's high-risk, predicted a wide scope and that broke my whole scripted demo because I'd built it expecting a different answer. Took me a second to realize the model was right and I was wrong. Had to rebuild the demo around a task that was actually narrow instead of fighting what the model was telling me. Also hit a dumb Windows bug where certain terminal characters would crash the CLI (fixed with ASCII-safe output), and a separate bug where wrapping the real codex exec command silently failed because I forgot to import something in a callback. Found that one by actually running it against the real tool instead of trusting my own tests.

### Accomplishments we're proud of

Getting it to actually wrap the real Codex CLI and catch a real file write live that was the moment it stopped feeling like a hackathon demo and started feeling like a real tool. Also proud that it's a genuinely installable PyPI package, not just something you have to clone and configure. And that when I found my own mistakes mid-build (like the MFA thing), I wrote them up honestly instead of just quietly fixing them and pretending it always worked.

### What we learned

That this quota-burn problem is real and actively being talked about right now, not something I invented to fit a hackathon category. And that being honest about what a feature can't do yet (like external mode not being able to prevent a write, only react to it) actually makes the whole project feel more trustworthy than oversell would.

### What's next

Getting deeper visibility into external processes right now the filesystem-watch mode can't see read-only file access or subprocess calls happening inside the wrapped tool. Support for other agent runtimes beyond Codex, like Claude Code. Maybe some kind of learning over time so predictions get sharper the more sessions it sees. And team-level policy stuff if people want to share budget guardrails across a group.

## README (from the GitHub repository)

# Foresight

**Foresight is a predictive cost and tier-routing layer for AI coding agents.** It chooses the right level of reasoning before work begins, then watches the session closely enough to stop drift before it becomes expensive.

![CI](https://github.com/AmanM006/foresight/actions/workflows/ci.yml/badge.svg?branch=main)
![MIT License](https://img.shields.io/badge/license-MIT-111827?style=flat-square)
![Python](https://img.shields.io/badge/Python-3.11%2B-3776AB?style=flat-square)
![Next.js](https://img.shields.io/badge/Next.js-14-111111?style=flat-square)

## Judge quick start

**No rebuild, API key, Node.js, or account is needed for the replay proof.** On a supported Windows machine:

```powershell
python -m pip install foresight-agent-guard==0.1.23
mkdir foresight-judge-demo
cd foresight-judge-demo
foresight demo --replay
```

For the complete deterministic suite and committed evidence, clone the repository and run `python verify.py`. The replay is the fastest judge path: it launches a real instrumented child, records an allowed action, flags scope drift, blocks a fake protected-path read, and writes a local audit without an OpenAI call.

## Supported platform and Codex local setup

Foresight 0.1.23 is a **Codex-only, local-first execution-contract layer**. This submission is tested on **Windows 10/11, Python 3.11 or 3.12, and the Codex CLI**. macOS/Linux package installation is supported by the Python package, but the Codex integration has not been submission-validated there.

In a repository you want to monitor, set it up once, then use `codex` normally:

```powershell
python -m pip install foresight-agent-guard==0.1.23
cd path\to\your-repository
foresight setup --codex
codex
```
`setup` copies the bundled plugin into a Foresight-owned project folder, merges a project-local Codex marketplace entry, adds only Foresight-owned hooks, and backs up an existing `hooks.json` or marketplace file before changing it. It never overwrites `AGENTS.md`, existing hooks, an existing marketplace entry, or an existing `.codex/config.toml`. Use `foresight setup --codex --preview` to see the exact local changes first. The real setup command registers the project marketplace and installs the plugin through the Codex CLI, so no Desktop plugin picker is required. You still review/trust hooks through Codex's `/hooks` flow; Foresight never bypasses that review.

| Guarantee | What Foresight does today |
| --- | --- |
| Known secret paths | Codex static deny rules block `.env`, keys, credentials, and configured protected paths before sandboxed access when Foresight's generated project config is active. |
| Contract lifecycle | A Codex skill/MCP surface compiles, stores, advances, pauses, and finishes a local execution contract. |
| Scope, cost, churn, fan-out | Local telemetry detects and reports drift against the active contract; dynamic intervention is post-tool/turn control, not a universal pre-execution claim. |
| Codex subagents | Documented `SubagentStart` / `SubagentStop` hooks record safe agent IDs and types in the contract. Exceeding the concurrent fan-out budget pauses the contract and injects context for later progress; it does not claim a universal pre-spawn hard block. |
| Strict interception | The advanced Python-child runtime remains the only process-local pre-execution file/subprocess/network interceptor. |

Without `OPENAI_API_KEY`, Foresight uses a clearly labelled local baseline: static secret-path protection, bounded defaults, local audit, and replay. With a key, `begin_contract` compiles policy, routing recommendation, phases, cost range, and escalation rules in **one** structured GPT-5.6-terra call.

Run `foresight doctor` at any time to inspect Codex detection, plugin registration/installation, hooks, static policy, and the required trust steps. Run `foresight uninstall` to remove only Foresight-owned integration artifacts while preserving later user edits.
### Local dashboard and durable history

`foresight dashboard` is an opt-in **local** control center. It starts a read-only HTTP server for the current repository, serves the dashboard bundled in the Python wheel, and connects it to the local event bus. No Node.js, account, cloud sync, or remote ingestion is required.

Every contract is retained without expiry in `.foresight/contracts/<session-id>.json`, with an append-only ordered event record in `.foresight/history/<session-id>.jsonl` and a compact session index in `.foresight/history/index.json`. Events first survive in the local spool during an event-bus outage, then the bus persists them before live browser delivery. Reopening the dashboard reloads history before it reconnects to the live stream.

The dashboard reports **Foresight API cost** only: exact token-derived cost for Foresight's own contract compiler calls. Codex's native ChatGPT quota/token display is intentionally shown as unavailable because Foresight has no official machine-readable billed-cost source for it. `foresight status` uses the same local contract store: it shows an active contract while Codex is working, otherwise the newest completed contract.

`init`, `run`, and `shell` are retained as advanced Python/external-runtime and demo commands. The normal Codex workflow does not require running `foresight run` before every task.

### Codex subagent control

Each active contract now keeps a local, safe subagent ledger: documented Codex `agent_id`, `agent_type`, turn ID, lifecycle state, and observed-action count. When concurrent active agents exceed the compiled contract's `fanout_budget`, Foresight emits `fanout_budget_exceeded`, changes the contract to `needs_confirmation`, and supplies stop context to the new subagent. A `PostToolUse` hook then stops further turn progress until the user confirms the expansion. Static secret-path denies remain the only pre-execution control; dynamic fan-out is deliberately described as a Codex hook/turn intervention.

See [the Codex integration guide](docs/CODEX_INTEGRATION.md) for the local artifact layout, guarantee matrix, and lifecycle-tool behavior.

For the full evidence files and zero-cost verification, run the current submission from source. It needs Python only: no API key, Node.js, or browser.

>
> ```bash
> git clone https://github.com/AmanM006/foresight.git
> cd foresight
> pip install -e .
> foresight demo --replay
> python verify.py
> ```
>
> The replay launches a real instrumented Python child, shows the terminal dashboard, flags scope drift, then blocks a fake `.env` read in under a minute. Use `foresight dashboard` to open the optional packaged local history viewer; it requires no Node.js.

> **PyPI release status**
>
> Install the current release with `pip install foresight-agent-guard==0.1.23`. The bundled Codex plugin supports normal Windows `pip --user` installations as well as source installs.
>

## Verify this submission

Run the complete replay-safe verification pass with one command:

```bash
python verify.py
```

It runs the Python-child replay and deterministic tests without spending tokens. Add `--live` only when you want it to run the paid tier and injection benchmarks as well. For a zero-setup visual tour only, explore the deployed [Foresight landing/demo site](https://sentry-lime.vercel.app); it is not connected to local sessions.

## The problem

When developers use Codex/GPT-5.6 for coding work, they often do not know upfront which tier or reasoning effort a task actually needs. Defaulting to Sol overpays for mechanical work; choosing too small a tier can leave an agent stuck and repeatedly escalating.

The risk is not hypothetical. An open [Codex issue #32250](https://github.com/openai/codex/issues/32250) reports GPT-5.6 Sol Medium reducing a Pro five-hour allowance from 87% to 76% during a short conversation and several trivial follow-ups. Recent [r/codex](https://www.reddit.com/r/codex/comments/1ust6y8/usage_drops_much_faster_now_despite_less_cost/) and [r/ChatGPTPro](https://www.reddit.com/r/Chat

[README truncated for size]

## Detected evidence (automated analysis)

Indexed codebase: 135 recognized source files, 4014 KB.
- CSS (language) — detected in the code
- HTML (language) — detected in the code
- JavaScript (language) — detected in the code
- Next.js (technology) — detected in the code
- OpenAI (technology) — detected in the code
- Python (language) — detected in the code
- React (technology) — detected in the code
- Tailwind CSS (technology) — detected in the code
- TypeScript (language) — detected in the code

## Codebase structure (from repository index)

### Files (120 of 167)

```
.env.example
.gitattributes
.github/workflows/ci.yml
.gitignore
action.yml
benchmark_results.json
docs/CODEX_INTEGRATION.md
docs/GITHUB_ACTION.md
docs/MANUAL_CODEX_PROOF.md
docs/PUBLISHING.md
effort_curve_live_evidence.json
examples/audit_event_output.example.json
examples/codex_contract_live_proof.json
examples/codex_contract_live_proof.py
examples/policy_and_verdict_output.example.json
examples/python_child_live_proof.json
examples/terminal_recording/README.md
examples/terminal_recording/sample.py
examples/unified_live_agent.py
examples/unified_live_run_evidence.json
examples/unified_live_run.py
foresight/__init__.py
foresight/agent_launcher.py
foresight/benchmark.py
foresight/child_runtime.py
foresight/cli.py
foresight/codex_adapter.py
foresight/codex_mcp.py
foresight/codex_setup.py
foresight/config.py
foresight/contracts.py
foresight/dashboard_assets/_next/static/chunks/117-754f967a3459e24a.js
foresight/dashboard_assets/_next/static/chunks/284-a82b8686e8c33f67.js
foresight/dashboard_assets/_next/static/chunks/972-dde10be5ed9a203e.js
foresight/dashboard_assets/_next/static/chunks/app/_not-found/page-990f8e70efc1a263.js
foresight/dashboard_assets/_next/static/chunks/app/dashboard/page-0d29319d69bdf86b.js
foresight/dashboard_assets/_next/static/chunks/app/dashboard/page-46a9674f04ef186d.js
foresight/dashboard_assets/_next/static/chunks/app/guidelines/page-fd532542fc0e852d.js
foresight/dashboard_assets/_next/static/chunks/app/layout-06b96e00e86d47e2.js
foresight/dashboard_assets/_next/static/chunks/app/page-905ca1f907442a13.js
foresight/dashboard_assets/_next/static/chunks/app/privacy/page-0f09583039952bf9.js
foresight/dashboard_assets/_next/static/chunks/app/terms/page-163e95673675de35.js
foresight/dashboard_assets/_next/static/chunks/fd9d1056-fffa46e3e31c51f3.js
foresight/dashboard_assets/_next/static/chunks/framework-f66176bb897dc684.js
foresight/dashboard_assets/_next/static/chunks/main-7735887475585191.js
foresight/dashboard_assets/_next/static/chunks/main-app-9ed4c2eed1550f1d.js
foresight/dashboard_assets/_next/static/chunks/pages/_app-72b849fbd24ac258.js
foresight/dashboard_assets/_next/static/chunks/pages/_error-7ba65e1336b92748.js
foresight/dashboard_assets/_next/static/chunks/polyfills-42372ed130431b0a.js
foresight/dashboard_assets/_next/static/chunks/webpack-03f7c6bc932ce1e3.js
foresight/dashboard_assets/_next/static/css/f8b031d0a1960132.css
foresight/dashboard_assets/_next/static/eR5Eh39yIWqmMfP3AQWPo/_buildManifest.js
foresight/dashboard_assets/_next/static/eR5Eh39yIWqmMfP3AQWPo/_ssgManifest.js
foresight/dashboard_assets/_next/static/fC3swI5oUICIC5V7iVoNh/_buildManifest.js
foresight/dashboard_assets/_next/static/fC3swI5oUICIC5V7iVoNh/_ssgManifest.js
foresight/dashboard_assets/_next/static/qThFKVtg4ElyD9wTL8T9M/_buildManifest.js
foresight/dashboard_assets/_next/static/qThFKVtg4ElyD9wTL8T9M/_ssgManifest.js
foresight/dashboard_assets/_next/static/xf7_kqa2Y9LlPFzJGQnpx/_buildManifest.js
foresight/dashboard_assets/_next/static/xf7_kqa2Y9LlPFzJGQnpx/_ssgManifest.js
foresight/dashboard_assets/404.html
foresight/dashboard_assets/dashboard.html
foresight/dashboard_assets/dashboard.txt
foresight/dashboard_assets/guidelines.html
foresight/dashboard_assets/guidelines.txt
foresight/dashboard_assets/index.html
foresight/dashboard_assets/index.txt
foresight/dashboard_assets/privacy.html
foresight/dashboard_assets/privacy.txt
foresight/dashboard_assets/terms.html
foresight/dashboard_assets/terms.txt
foresight/dashboard_server.py
foresight/demo_scenario.py
foresight/eventbus_bridge.py
foresight/external_monitor.py
foresight/guardrail_scenarios.py
foresight/history.py
foresight/injection_benchmark.py
foresight/interactive_session.py
foresight/live_proof_agent.py
foresight/local_runtime.py
foresight/multi_task_session.py
foresight/predictor.py
foresight/replay_agent.py
foresight/replay_capture.json
foresight/session_monitor.py
foresight/terminal_dashboard.py
foresight/workflow_demo.py
foresight/workflow_plan_benchmark.py
guardrail_scenarios_results.json
injection_benchmark_results.json
LICENSE
MANIFEST.in
multi_task_session_results.json
plugins/foresight-codex/.codex-plugin/plugin.json
plugins/foresight-codex/.mcp.json
plugins/foresight-codex/skills/foresight-contract/SKILL.md
pyproject.toml
README.md
requirements.txt
scripts/run_ci_guard.sh
sentry/__init__.py
sentry/demo/demo_scenario.py
sentry/eventbus/__init__.py
sentry/eventbus/server.py
sentry/policy/__init__.py
sentry/policy/engine.py
sentry/policy/schema.py
sentry/proxy/__init__.py
sentry/proxy/actions.py
sentry/proxy/interceptor.py
sentry/README.md
sentry/requirements.txt
sentry/web/.gitignore
sentry/web/app/dashboard/page.tsx
sentry/web/app/globals.css
sentry/web/app/guidelines/page.tsx
sentry/web/app/layout.tsx
sentry/web/app/page.tsx
sentry/web/app/privacy/page.tsx
sentry/web/app/terms/page.tsx
[47 more files omitted for size]
```

### Dependencies

- pyproject.toml: click@>=8.1,<9, openai@>=1.0,<3, psutil@>=5.9,<8, python-dotenv@>=1.0,<2, requests@>=2.31,<3, rich@>=13.0,<15, watchdog@>=4,<7, websockets@>=13,<16
- requirements.txt: click@>=8.1,<9, openai@>=1.0,<3, psutil@>=5.9,<8, python-dotenv@>=1.0,<2, requests@>=2.31,<3, rich@>=13.0,<15, watchdog@>=4,<7, websockets@>=13,<16
- sentry/requirements.txt: openai, python-dotenv, websockets
- sentry/web/package.json: @types/node@^20, @types/react@^18, @types/react-dom@^18, lenis@^1.3.25, next@14.2.35, postcss@^8, react@^18, react-dom@^18, tailwindcss@^3.4.1, typescript@^5

### Recent commits (newest first)

- Avoid counting Foresight MCP lifecycle tools as actions
- Release real Codex contract cost tracking 0.1.22
- Silence expected local event bus handshake noise
- Fix Windows Codex local skill registration
- Fix Windows Codex hooks and local skill loading
- Polish Codex-first CLI welcome flow
- Separate Foresight dashboard monitor views
- Fix local landing route and refresh Foresight hero
- Finalize dashboard evidence in README
- Restore original dashboard design and release 0.1.15
- Document Foresight 0.1.14 release
- Add persistent local Codex dashboard
- Enable Windows workspace sandbox for Codex setup
- Fix cross-platform Codex launcher test
- Fix Codex MCP newline framing
- Pin Windows Codex MCP launcher to Python
- Migrate mixed legacy Codex hook configs
- Fix Codex hook schema and marketplace collisions
- Install Codex plugin directly from setup
- Make CI guard fixture cross-platform

## Key source files (fetched from GitHub, selected and truncated for size)

### docs/GITHUB_ACTION.md

```markdown
# GitHub Action

`Foresight Agent Guard` wraps an AI-agent command in a GitHub Actions job. It derives a policy from `task_scope`, runs the command through `foresight run`, and exposes the final report as Action outputs.

```yaml
- uses: AmanM006/foresight@main
  with:
    task_scope: "Fix the failing test in ./src/auth, no other files"
    command: "codex exec 'fix the failing test'"
    openai_api_key: ${{ secrets.OPENAI_API_KEY }}
    fail_on_violation: true
```

## Outputs

| Output | Meaning |
| --- | --- |
| `files_touched` | Number of files recorded in the final report. |
| `flags_raised` | JSON list of predictive monitor flags. |
| `final_verdict` | Final Foresight verdict. |
| `total_cost` | Foresight-side GPT policy/prediction cost in USD. |

Example use in a later step:

```yaml
- run: echo "${{ steps.guard.outputs.final_verdict }}"
```

## CI limitations

The external-command path uses post-hoc filesystem monitoring: writes and deletes are detected after they occur, then a critical violation terminates the tracked process tree. For Python agents, prefer `foresight run -- python agent.py` for the fuller pre-execution interception path.

The `command` input is evaluated as a shell command so quoted agent prompts work. Treat workflow YAML as trusted code and do not populate it from untrusted pull-request text.

```

### docs/MANUAL_CODEX_PROOF.md

```markdown
# Manual Codex contract proof

This checklist creates one human-operated, local-only proof. It is intentionally
not automated: Codex hook trust and ChatGPT Desktop interaction require a real
interactive Codex surface.

## CLI proof

1. Create a disposable repository containing `src/auth/login.py` and a fake
   `.env` file. Do not put real credentials in the fixture.
2. From that repository, run:

   ```powershell
   foresight setup --codex --preview
   foresight setup --codex
   foresight doctor
   ```

3. Start `codex`, open `/hooks`, review the Foresight entries, and trust them.
4. Give Codex this task:

   ```text
   Use the Foresight execution-contract tools. Begin a contract to rename one
   local variable in src/auth/login.py only. Make and verify the edit, request
   scope expansion before touching any second file, then finish the contract.
   ```

5. In another terminal in the same repository, run:

   ```powershell
   foresight status
   foresight dashboard
   ```

6. Confirm the same session ID appears in `.foresight/contracts/`,
   `.foresight/history/`, `foresight status`, and the dashboard. Capture the
   allowed edit, scope-expansion transition, final report, and the local audit
   timeline. Do not capture secret contents.

7. For the static-boundary proof, begin a separate contract asking Codex to
   read `.env`. Confirm Codex refuses because the generated protected-path
   policy applies; do not ask it to bypass the rule.

## ChatGPT Desktop configuration check

Repeat steps 1-3 in ChatGPT Desktop using the same repository and verify that
the generated project integration is discovered, hooks are reviewable, and the
Foresight skill/MCP tools are available. Only claim full Desktop parity after
the lifecycle in steps 4-6 is also observed there.

## Evidence to retain

- One session ID and sanitized contract JSON.
- A screenshot of the local dashboard showing the same session and final state.
- The final `foresight status` output.
- The Codex `/hooks` review screen, with no sensitive paths or secrets shown.

```

### requirements.txt

```
click>=8.1,<9
openai>=1.0,<3
python-dotenv>=1.0,<2
requests>=2.31,<3
rich>=13.0,<15
websockets>=13,<16
watchdog>=4,<7
psutil>=5.9,<8

```

### pyproject.toml

```
[build-system]
requires = ["setuptools>=68"]
build-backend = "setuptools.build_meta"

[project]
name = "foresight-agent-guard"
version = "0.1.23"
description = "Predictive cost, policy, and runaway-session guardrails for AI coding agents"
readme = "README.md"
license = {text = "MIT"}
authors = [{name = "Aman M"}]
requires-python = ">=3.11"
keywords = ["ai-agents", "codex", "gpt", "cost-monitoring", "developer-tools"]
classifiers = [
    "Development Status :: 3 - Alpha",
    "Intended Audience :: Developers",
    "License :: OSI Approved :: MIT License",
    "Operating System :: OS Independent",
    "Programming Language :: Python :: 3",
    "Programming Language :: Python :: 3.11",
    "Programming Language :: Python :: 3.12",
    "Topic :: Software Development",
]
dependencies = [
    "click>=8.1,<9",
    "openai>=1.0,<3",
    "python-dotenv>=1.0,<2",
    "requests>=2.31,<3",
    "rich>=13.0,<15",
    "websockets>=13,<16",
    "watchdog>=4,<7",
    "psutil>=5.9,<8",
]

[project.urls]
Homepage = "https://github.com/AmanM006/foresight"
Repository = "https://github.com/AmanM006/foresight"
Issues = "https://github.com/AmanM006/foresight/issues"

[project.scripts]
foresight = "foresight.cli:main"

[tool.setuptools]
include-package-data = false

[tool.setuptools.packages.find]
include = ["foresight*", "sentry*"]
exclude = ["foresight.src*", "sentry.web*"]
namespaces = false

[tool.setuptools.package-data]
foresight = ["replay_capture.json", "dashboard_assets/**/*"]

[tool.setuptools.data-files]
"share/foresight/codex-plugin" = ["plugins/foresight-codex/.mcp.json"]
"share/foresight/codex-plugin/.codex-plugin" = ["plugins/foresight-codex/.codex-plugin/plugin.json"]
"share/foresight/codex-plugin/skills/foresight-contract" = ["plugins/foresight-codex/skills/foresight-contract/SKILL.md"]

```

### sentry/requirements.txt

```
websockets
openai
python-dotenv


```

### sentry/web/package.json

```
{
  "name": "web",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint"
  },
  "dependencies": {
    "lenis": "^1.3.25",
    "next": "14.2.35",
    "react": "^18",
    "react-dom": "^18"
  },
  "devDependencies": {
    "@types/node": "^20",
    "@types/react": "^18",
    "@types/react-dom": "^18",
    "postcss": "^8",
    "tailwindcss": "^3.4.1",
    "typescript": "^5"
  }
}

```

### sentry/eventbus/server.py

```python
"""WebSocket event bus and broadcast helpers for Sentry.

Exposes:
    broadcast(action, verdict)      – called as on_action_callback by SentryProxy
    broadcast_cost(cost_event)      – called as on_cost_event by CostTracker
    export_audit_log()              – writes session audit JSON to disk
    start_server(host, port)        – run the WebSocket server (coroutine)

Message shapes emitted to clients
──────────────────────────────────
Action event:
    {
        "type": "action",
        "action":  { ...asdict(Action)  },
        "verdict": { ...asdict(Verdict) },
        "timestamp": <float unix seconds>
    }

Cost event:
    {
        "type": "cost",
        "cost_event": { ...asdict(CostEvent) }
    }
"""

from __future__ import annotations

import asyncio
import json
import logging
import re
import time
import os
from dataclasses import asdict
from pathlib import Path
from collections.abc import Callable
from typing import Any

import websockets
import websockets.asyncio.server
from websockets.sync.client import connect as websocket_connect

from sentry.policy.schema import CostEvent, Verdict
from sentry.policy.engine import CostTracker
from sentry.policy.schema import Policy
from sentry.proxy.actions import Action
from sentry.proxy.interceptor import SentryProxy
from foresight.history import HistoryStore


# ---------------------------------------------------------------------------
# Module-level state
# ---------------------------------------------------------------------------

DEFAULT_PORT: int = 8765

audit_log: list[dict[str, Any]] = []
server_log: list[str] = []
_clients: set[websockets.asyncio.server.ServerConnection] = set()
_event_loop: asyncio.AbstractEventLoop | None = None
_session_id: str = "unknown"
_listeners: set[Callable[[dict[str, Any], str], None]] = set()
# Dashboard clients commonly connect after the CLI has emitted setup metadata.
# Keep a tiny latest-value snapshot instead of replaying the full audit log and
# accidentally duplicating action/cost totals after a reconnect.
_session_context: dict[str, dict[str, Any]] = {}


class _HandshakeNoiseFilter(logging.Filter):
    """Hide expected TCP probes that close before a WebSocket handshake."""

    def filter(self, record: logging.LogRecord) -> bool:
        return record.getMessage() != "opening handshake failed"


_WEBSOCKET_LOGGER = logging.getLogger("foresight.eventbus.websocket")
_WEBSOCKET_LOGGER.addFilter(_HandshakeNoiseFilter())


# ---------------------------------------------------------------------------
# Public broadcast helpers (called from any thread)
# ---------------------------------------------------------------------------

def broadcast(action: Action, verdict: Verdict) -> None:
    """Publish an action verdict to connected clients and append to audit_log.

    This is designed to be passed as *on_action_callback* to SentryProxy.
    It is safe to call from a non-async thread.
    """
    global _session_id
    _session_id = action.agent_session_id

    message: dict[str, Any] = {
        "type": "action",
        "action": asdict(action),
        "verdict": asdict(verdict),
        "timestamp": time.time(),
    }
    _publish(message, "local")


def broadcast_cost(cost_event: CostEvent) -> None:
    """Publish a model-cost event to connected clients and append to audit_log.

    This is designed to be passed as *on_cost_event* to CostTracker.
    It is safe to call from a non-async thread.
    """
    message: dict[str, Any] = {
        "type": "cost",
        "cost_event": asdict(cost_event),
    }
    _publish(message, "local")


def broadcast_prediction(predicted: dict[str, Any], session_id: str | None = None) -> None:
    """Publish a Foresight complexity prediction to dashboards.

    ``session_id`` is optional because setup-time predictions can exist before
    a monitored child has been assigned an ID.
    """
    message: dict[str, Any] = {
        "type": "prediction",
        "prediction": predicted,
        "timestamp": time.time(),
    }
    if session_id is not None:
        message["session_id"] = session_id
    _publish(message, "local")


def broadcast_workflow_plan(plan: dict[str, Any], session_id: str | None = None) -> None:
    """Publish an optional Foresight phase plan to connected dashboards."""

    message: dict[str, Any] = {
        "type": "workflow_plan",
        "workflow_plan": plan,
        "timestamp": time.time(),
    }
    if session_id is not None:
        message["session_id"] = session_id
    _publish(message, "local")


def broadcast_signal(
    flag_type: str,
    details: dict[str, Any],
    session_id: str | None = None,
) -> None:
    """Publish a Foresight monitor signal to dashboards."""
    message: dict[str, Any] = {
        "type": "signal",
        "flag_type": flag_type,
        "details": details,
        "timestamp": time.time(),
    }
    if session_id is not None:
        message["session_id"] = session_id
    _publish(message, "local")


def broadcast_burn_forecast(
    forecast: dict[str, float], session_id: str | None = None
) -> None:
    """Publish a local burn-rate forecast without escalating or killing."""
    message: dict[str, Any] = {
        "type": "burn_forecast",
        **forecast,
        "timestamp": time.time(),
    }
    if session_id is not None:
        message["session_id"] = session_id
    _publish(message, "local")


def broadcast_policy(policy: Policy, session_id: str) -> None:
    """Publish the exact policy bound to one monitored session."""

    _publish(
        {
            "type": "policy",
            "session_id": session_id,
            "policy": asdict(policy),
            "timestamp": time.time(),
        },
        "local",
    )


def broadcast_session_report(report: dict[str, Any], session_id: str) -> None:
    """Publish the monitor's terminal report for one monitored session."""

    _publish(
        {
            "type": "session_report",
            "session_id": session_id,
            "re
[truncated — 6942 more characters]
```

### sentry/web/app/layout.tsx

```typescript
import type { Metadata } from "next";
import "./globals.css";

export const metadata: Metadata = {
  title: "Foresight - Codex execution contracts",
  description: "Local execution contracts, audit history, and boundary monitoring for Codex.",
};

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en" className="dark">
      <body className="min-h-screen bg-[#07070c] text-[#f0f0f8] antialiased">
        {children}
      </body>
    </html>
  );
}

```

### sentry/web/app/page.tsx

```typescript
import LenisProvider from "@/components/LenisProvider";
import Navbar from "@/components/landing/Navbar";
import Hero from "@/components/landing/Hero";
import Features from "@/components/landing/Features";
import CodexFAQ from "@/components/landing/CodexFAQ";
import ForesightFooter from "@/components/landing/ForesightFooter";

export default function LandingPage() {
  return (
    <LenisProvider>
      {/* 
        Base container with the clean light off-white bg #fbfcfd.
      */}
      <div 
        className="relative min-h-screen text-[#2d2e38] overflow-x-hidden bg-[#fbfcfd]"
        style={{
          backgroundImage: "linear-gradient(to bottom, transparent 90vh, #fbfcfd 150vh)",
          backgroundAttachment: "scroll",
        }}
      >
        
        {/* 
          150vh SentryHeroBackground container.
          Fades out smoothly into solid off-white background after 100vh.
        */}
        <div 
          className="absolute inset-x-0 top-0 overflow-hidden pointer-events-none z-0"
          style={{
            height: "120vh",
            maskImage: "linear-gradient(to bottom, black 0px, black 80vh, transparent 120vh)",
            WebkitMaskImage: "linear-gradient(to bottom, black 0px, black 80vh, transparent 120vh)",
          }}
        >
          {/* 1. Cliffline Background Gradient Image (Centered directly behind the Hero fold) */}
          <div 
            className="absolute z-0 overflow-visible"
            style={{
              top: "-80px",
              left: "50%",
              transform: "translateX(-50%)",
              width: "180%",
              maxWidth: "1800px",
              aspectRatio: "1.977",
              opacity: 0.85,
              maskImage: "radial-gradient(ellipse 50% 50% at 50% 50%, black 30%, transparent 100%)",
              WebkitMaskImage: "radial-gradient(ellipse 50% 50% at 50% 50%, black 30%, transparent 100%)",
            }}
          >
            <img 
              src="/images/gTc9aNNWSVTqmlbrospda6CfC5I.png" 
              alt="Cliffline Background Gradient" 
              className="w-full h-full object-contain"
            />
          </div>

          {/* 2. Faint Premium Grid Overlay */}
          <div 
            className="absolute inset-0 z-10"
            style={{
              // Draws the premium faint grey grid lines
              backgroundImage: `
                linear-gradient(to right, rgba(9, 9, 11, 0.02) 1px, transparent 1px),
                linear-gradient(to bottom, rgba(9, 9, 11, 0.02) 1px, transparent 1px)
              `,
              backgroundSize: '48px 48px',
              // Fades the grid out at the edges
              WebkitMaskImage: 'radial-gradient(ellipse 60% 50% at 50% 50%, black 40%, transparent 100%)',
              maskImage: 'radial-gradient(ellipse 60% 50% at 50% 50%, black 40%, transparent 100%)',
            }}
          ></div>
        </div>

        {/* Content layers */}
        <div className="relative z-10 bg-transparent">
          <Navbar />
          <main className="bg-transparent">
            <Hero />
            <Features />
            <CodexFAQ />
            <ForesightFooter />
          </main>
        </div>
      </div>
    </LenisProvider>
  );
}

```

### sentry/web/app/guidelines/page.tsx

```typescript
import LegalPage from "@/components/legal/LegalPage";

export default function GuidelinesPage() {
  return (
    <LegalPage
      eyebrow="Product guidance"
      title="Usage Guidelines"
      updated="July 16, 2026"
      intro="Use Sentry as an additional visibility and enforcement layer around AI coding agents. These guidelines help keep a monitored session interpretable and useful."
      sections={[
        {
          title: "Start with a narrow task",
          paragraphs: ["Describe the intended files, expected change, and any prohibited operations before starting a session. Narrow scopes create clearer predictions and make scope-drift signals meaningful."],
        },
        {
          title: "Treat signals differently",
          paragraphs: ["A blocked critical action is deterministic enforcement. Scope-explosion and burn-forecast signals are monitoring signals that should prompt review or reassessment. Do not treat every warning as proof of malicious behavior."],
        },
        {
          title: "Keep the guardrails on",
          paragraphs: ["Run the agent through the Foresight CLI so the Sentry proxy can observe its supported Python child process. Review the terminal dashboard or web dashboard while the session is active."],
          bullets: [
            "Use explicit allowed paths and hosts.",
            "Do not disable forbidden-pattern checks to make a demo pass.",
            "Stop and inspect a session before overriding a critical block.",
          ],
        },
        {
          title: "Know the current boundary",
          paragraphs: ["The packaged interception path currently targets Python child processes. Other agent runtimes remain a roadmap item, so do not assume uninstrumented commands are being monitored."],
        },
      ]}
    />
  );
}

```

[113 more indexed source files omitted to keep this export small. The full file list is in the Codebase structure section above.]