Skip to main content
Glama
tern-dot-bridge

tern-dot-bridge

by lfsmoura
README.md
# tern-dot

A private, local-first, **read-only** awareness channel from [Tern](https://stencil.so/tern) coding sessions to your ChatGPT dot. It answers:
- what's running;
- which sessions need you, and why (as far as the evidence allows);
- what verifiably finished or failed;
- whether any of this is stale.

It never answers agents, approves anything, or types into, starts or stops a session.

```text
Tern window observer(s) ──loopback, authenticated──▶ tern-dot-bridge (your Mac)
   tern-plugin/                                       ├─ reducer: activity · outcome · freshness
                                                      ├─ SQLite store + per-subscription outbox
                                                      ├─ MCP Events (webhook) + 5 read-only MCP tools
                                                      └─ single-user OAuth 2.1 server
        ChatGPT ──Secure MCP Tunnel / approved HTTPS──▶ public MCP listener
        ChatGPT ◀──signed Standard Webhooks callbacks── delivery worker
```

## Status

| Area | State |
| --- | --- |
| Bridge, reducer, store, outbox, MCP tools, MCP Events, OAuth server, Tern observer | Implemented and tested locally. `npm run check` runs typechecking, schema drift checks, Node tests, Luau analysis and Luau tests. |
| Full pipeline with mock observer + mock receiver | `npm run bridge -- simulate`: every stage passed except stage 5 (dot display), which was not run. |
| Live Tern plugin, ChatGPT connection, dot delivery | **Not run.** Needs your approval and access; see [Setup §5–8](docs/setup.md). The integration is **not** connected yet. |

## Quick start (local only)

```sh
npm ci
npm run check
npm run bridge -- simulate
```

Then follow [docs/setup.md](docs/setup.md) to configure projects, initialize secrets, start the bridge and, with approval, link the Tern plugin and connect ChatGPT.

## What the dot can rely on

- **Separate dimensions.** Activity (`running`, `waiting_input`, `idle`, `exited`, `unknown`), outcome (with scope and evidence) and freshness are reported separately.
  - "Completed" means the agent reported a successful turn through Tern's `agent_finished` event. It is never inferred from idle or exit.
  - A missing session is "observation lost", never success.
- **Quiet by default.** Each client subscribes once to `tern.updates` with the event types it wants. Input requests and failures are sent immediately; turn completions are sent individually. Everything else is batched into at most one digest per 10 minutes.
- **No stale exports.** Before each attempt, queued work is checked against the client's current type/filter selection and project policy. Disabling previews strips queued text; digests are rebuilt without disabled projects.
- **No repeats.** One unresolved request means one alert, and an alert whose request resolved before delivery is suppressed. Revoked or expired subscriptions get nothing from old queues.
- **Metadata only by default.** No paths, commands, pane ids or agent text. Per-project opt-in previews are capped at 500 characters and redacted. Transcripts are never exported.

## Documentation

- [Architecture and state semantics](docs/architecture.md)
- [Compatibility matrix](docs/compatibility.md), covering what is verified, declared, untested or unsupported
- [Privacy and threat model](docs/threat-model.md)
- [Setup, operation and troubleshooting](docs/setup.md)
- [Verification report](docs/verification-report.md)
- [Contract research notes](docs/research-notes.md)
- [Example configuration](examples/config.example.jsonc), with published schemas in [`schemas/`](schemas)
- Tern observer: [tern-plugin/README.md](tern-plugin/README.md)
- ChatGPT plugin package and skill: [chatgpt-plugin/](chatgpt-plugin)

## Layout

```text
bridge/src/        bridge (ingest, domain, events, mcp, oauth, privacy, store, cli)
bridge/test/       node:test suites (state, events, privacy/security, tools, oauth, config)
tern-plugin/       Tern window plugin (Luau) + Luau tests
chatgpt-plugin/    portable plugin manifest + skill
schemas/           JSON Schemas generated from the runtime validators
tests/fixtures/    input-classification corpus, redaction fixtures
examples/          commented configuration
docs/              design, security, setup, verification
```

## Credits

The observer's polling pattern is adapted from [tern-haptic-alert](https://github.com/lfsmoura/tern-haptic-alert) (MIT, © 2026 Leonardo Moura); see `tern-plugin/NOTICE`. That plugin was not installed, modified or depended on.