pi-tern
Health Uyari
- License — License: MIT
- Description — Repository has a description
- Active repo — Last push 0 days ago
- Low visibility — Only 7 GitHub stars
Code Basarisiz
- exec() — Shell command execution in lib/bridge-plugin.ts
- process.env — Environment variable access in lib/fleet.ts
Permissions Gecti
- Permissions — No dangerous permissions requested
Bu listing icin henuz AI raporu yok.
Tern integration for the pi coding agent: TSP handshake, native Mermaid diagrams, WKWebView browser automation, pane capture and a session mirror.
pi-tern
Tern integration for the pi coding agent.
pi is a terminal coding agent. Tern is a terminal by Stencil Labs that renders native UI for programs that speak the Tern Surface Protocol (TSP): Mermaid diagrams, a WKWebView browser, native session surfaces. Programs that do not speak TSP are plain terminal panes.
This extension makes pi Tern-aware without patching pi:
- runs the TSP
hellohandshake, so pi knows it is inside Tern and what Tern supports; - renders Mermaid through Tern's built-in engine (merman) in a file block;
- drives Tern's built-in browser as a picture-in-picture and captures PNGs the model can see;
- captures panes and mirrors the conversation into a Markdown block;
- keeps the tab title live (
π <model> · <context> · <dir>) and can ring Tern's attention bell.
The extension itself never writes TSP frames, so pi's renderer is not disturbed — that is a deliberate boundary, since pi-tui's frames are multi-write synchronized-output transactions and a second writer tears them.
Native surfaces are a separate opt-in path, shipped in v1.0.0 and reached through the pi-tern launcher rather than the extension: the launcher probes Tern, and only when Tern has confirmed it is in an agent block does it run pi through a loader hook that hands rendering to Tern. Native mode is the special case, not the default — in a shell block, outside Tern, or with -p / --mode json|rpc, it runs stock pi. See Native surfaces need an agent block and docs/NATIVE-FINDINGS.md.
Features
| Feature | Status | What it does |
|---|---|---|
| TSP handshake | stable | Sends hello + DA1 on the pty and reads the reply through pi's raw input. Verified against Tern 0.9.8 (f21d9a3), whose reply advertises 46 node kinds and 9 features — 0.5.x's 44/10 with row/media added and program-palette dropped — and against 0.5.x, whose 44/10 reply still parses. The hello is feature-detected rather than pinned: any v >= 1 is used (a newer one is flagged, not refused), unknown kinds/features are reported instead of dropped, and a reply this build cannot read records why in /tern doctor instead of reporting absent. That is Tern's vocabulary, not pi-tern's usage — pi-tern gates on three features (image, chart, aside) and emits seven node kinds. Measurements: docs/TERN-0.9.md |
| Tern version negotiation | stable | tern --version is parsed (0.9.8 (f21d9a3) → version + commit) and the 0.9-only verbs are gated with a reason that names the version needed, including in the capability manifest. A 0.5.x Tern keeps every 1.1 feature and simply reports the newer ones as unavailable. PI_TERN_FORCE=1 bypasses the gate for a fork that backports a verb |
| Session identity | stable | tern_session whoami and /tern whoami: Tern 0.9's identity chain (principal, host, method, every hop) from TERN_IDENTITY when the pane exports it, else tern whoami --json. This is the first honest answer to "who and where is this pane" — 0.5-era code inferred it from TERM_PROGRAM and a socket path, which cannot tell a local pane from an SSH one. Also reported in tern_status, tern_diagnose and the manifest |
| Pane processes | stable | tern_session process reads tern process --json: the pid, argv and cwd of the block's program and what holds the terminal now. tern_diagnose includes it for the current pane, so "what is this pane actually running" stops being a capture-and-read |
| Session layout | experimental | `tern_session pip |
| Mermaid diagrams | stable | tern_diagram / /tern diagram opens a file block rendered by Tern's merman engine (18 diagram types, including xychart-beta bar/line charts). --last uses the newest fence in the conversation; pin keeps one path so re-renders update the same block |
| Charts | stable | tern_chart draws bar/hbar, line, area, pie and donut from plain data as a themed SVG that Tern renders as a native image block. png: true rasterizes it locally so the model can read the chart back. Value labels keep full precision |
| Fleet panes | stable | tern_fleet treats a Tern pane as a thread: spawn (a pi -p task, an interactive pi, or any command) · list with liveness · send to steer · read · wait · stop · prune. Task text goes through a file, so prompts cannot break the shell. Panes are spawned with --keep-open so a finished task's output survives; prune closes those that exited and have been idle past a floor (default 30m), reporting why it kept each one it did not touch. dryRun: true first |
| Git worktrees | stable | tern_worktree list/create/remove/prune/status with dirty counts and optional startFromOrigin. Pure git — works with no Tern at all |
| Pull requests | stable | tern_pr summary (checks collapsed to counts plus failing names, plus a one-line verdict), list, comments, watch (gh pr checks --watch in a visible pane), open in Tern's browser |
| Capability contract | stable | tern_status { manifest: true } / /tern capabilities: one machine-readable document listing every capability, whether it is available here, and why not |
| Runnable shell blocks | stable | tern_run / /tern run [--last] runs a bash/sh command from the conversation in a visible Tern pane, waits for exit and returns the captured output (PI_TERN_RUN=0 disables) |
| Session mirror | stable | /tern mirror on writes this conversation to session-mirror.md and opens it as a Tern block; a session TOC, timestamps and tool lines are included, and Mermaid inside it renders natively |
| Data plane | stable | tern_db (SQLite read-only by default), tern_doc (live documents, unsaved edits, heading-aware edits), tern_board (native task board lanes/cards) through Tern's own window APIs via the pi-bridge mailbox |
| Carly integration | stable | tern_carly + /tern ask|remember|recall|schedule|tasks|cancel; a pi_tern() export lets Carly read pi's status. Carly uses remote providers: never send vault/secret material |
| Notebooks & settings | experimental | tern_notebook read <pane> for open notebook blocks (execution is not exposed by Tern's plugin API); tern_settings get|list|describe |
| Tern browser | stable | tern_browser drives Tern's WKWebView picture-in-picture: open, state, snapshot, act, eval, capture (returns an image), input, goto, nav, events, close |
| Pane inspection | stable | tern_capture (text, ANSI, HTML, scrollback, surfaces), tern_panes, tern_ctl |
| Pane events | stable | tern_watch waits for daemon events (pane_exited, …) with an optional pane filter, so pi can wait for a test run or server instead of polling |
| Pane output waits | stable | tern_watch --expect <regex> polls a pane until it prints a pattern (dev servers, builds) and returns the tail |
| Browser suite | stable | relay/CLI ops, stale-ref recovery, named PNG baselines, network capture (Resource Timing HAR-lite), PDF export, multi-tab listing/close, form-field helper |
| pi-bridge canvas | experimental | linked automatically on the first Tern session — installing pi-tern is enough; renders a native Markdown dashboard with a session TOC and recent activity (ctrl+shift+f10); PI_TERN_BRIDGE=0 disables. tern_bridge status now reports Tern's own load verdict (status, problems) instead of only a version match |
| Plugin API check | stable | tern_bridge types runs tern plugin types (0.9+) and compares the declaration with every tern.* and cx.*:method the shipped Luau uses, so API drift is a message instead of a panel that quietly stops updating. scripts/plugin-api-check.mjs does the same from a terminal, against a pinned 0.9.8 fixture or the live build |
| Tool results panel | experimental | a second Tern plugin rendering what your tools produced as native widgets: a git diff as a real diff view, a file read as highlighted code, a test run as a pass/fail/skip meter. Open with ctrl+shift+f11. Classified by lib/toolresults.ts; falls back to muted text when nothing fits, because a confident-looking 0/0/0 meter is worse than the text it replaced |
| Golden shots | stable | tern_shot renders scenario files to PNG + layout JSON (tern shot) |
| UI test harness | stable | tern_ui_test / /tern ui-test <scenario> [expect] runs a scenario and asserts against a control endpoint |
| Diagram pipelines | stable | --from "<cmd>" renders mermaid from a command's output; git renders the repo as a gitGraph |
| Credential guard | stable | tern_db refuses credential-looking tables and agent/models stores unless allowSecret:true |
| Remote hosts | stable | tern_remote lists or discovers Tern remote hosts |
| Diagnostics | stable | tern_diagnose and /tern diagnose: environment, Tern release and TSP negotiation, the identity chain, what this pane runs, the plugin's load status, relay round trip, control endpoint, persisted state. /tern doctor --json is the machine-readable manifest |
| Control endpoint | stable | `/tern control [window |
| Restore | stable | /tern restore reopens the session mirror and the last pinned diagram; state survives Tern/pi restarts |
| Live tab title | stable | π <model> · <context> · <dir>, refreshed after startup and on every turn |
| Attention bell | opt-in | PI_TERN_BELL=1 rings Tern's bell when pi finishes a turn |
| Native Tern surfaces | opt-in | Tern owning the transcript/composer/dock. Reached through the pi-tern launcher, not the extension: pi runs under a loader hook that hands rendering to Tern. Only in an agent block, and only inside Tern — see Limitations |
| pi-bolt hosts | stable | Every host path resolves the agent binary from evidence (PI_TERN_AGENT, then PI_TERN_LAUNCHER, then pi, then pi-bolt) instead of hard-coding pi, so tern_fleet works on a machine whose host is pi-bolt — the same package at the same version under a different name. test/host-load.test.ts loads this extension under every installed host and asserts /tern registers |
Requirements
- Tern 0.5.1 or newer, running (closed beta; a Stencil account comes from Tern itself). Verified against 0.9.8 (
f21d9a3) on 2026-10-31: the TSP reply is stillv: 1with 46 kinds and 9 features, the surface ops pi-tern sends (o,add,set,del, thecol/rows/md/image/chart/card/asidenodes) are all accepted andblobis still rejected, the daemon relay handshake is unchanged, and the shipped Luau is declared-compatible withtern plugin types(79 symbols, 0 missing). 0.9's new verbs (whoami,process,pip,dock,plugin types) are gated on the version and reported as unavailable below it. Full measurements and the commands that produced them:docs/TERN-0.9.md. - pi 1.0 or newer, stock or pi-bolt (same
@earendil-works/pi-coding-agentpackage; the devDependency pins the runtime this is typechecked against). Nothing assumes the executable is namedpi. - macOS or Linux. Not functional inside tmux, screen or zellij: those swallow APC strings, so the TSP handshake never completes and the pane-only features stay inert. The CLI-backed tools (
tern_chart,tern_worktree,tern_pr) do not depend on the handshake and keep working.
Do I need to run pi inside Tern?
No. Only the pane-scoped features do (TSP probe, live tab title, relay push, native
surfaces). Everything else needs just a running Tern daemon, so a pi-tern tool from a
T3-hosted agent, a cron job or a plain terminal can still open diagrams and charts in the
Tern window, drive Tern's browser, run and capture panes, spawn fleet panes, and manage
worktrees and PRs. That is what /tern capabilities reports per feature, with a reason when
a feature is unavailable here.
Native surfaces need an agent block, not a shell block
This is the one thing that will look broken if you get it wrong. Tern displays a TSP surface only in
an agent block. Started in a shell block, pi-tern's frames are accepted and the surface
materialises — but nothing is ever drawn, so the pane is blank, with no error anywhere to read.
agent block → pi's transcript, composer and status render as Tern surfaces ✔
shell block → blank pane, no error, no clue �’
So either:
Open an agent block deliberately — Tern's palette can create one beside the focused pane, or
Run
/tern agent-setupinside pi. It writes the two settings Tern documents for this, after
backing up the file, and reports exactly what changed:Key Tern's description Value new_blocks"what new tabs and splits open" "Agent"agent_command"what a block runs: the login shell, or this" (defaults to omp)the pi-tern launcher Or set them by hand:
// ~/Library/Application Support/Tern/settings.json
{ "agent_command": "/Users/you/bin/pi-tern", "agent_placement": "split" }
And you can verify which kind you are in: /tern doctor reports the block kind, and pi-tern warns
when it detects a shell block.
Since 1.1.5 pi-tern detects this and recovers. At session start it asks the
pi-bridgeplugin
what kind of block it is in; if it is not an agent block it hands the terminal back to pi's own
interface and tells you why, instead of leaving a blank pane.PI_TERN_SKIP_KIND_CHECK=1disables
the check. The kind is only exposed to a window plugin (cx.session:panes()), which is why the
check goes through the mailbox —tern inspect --jsoncarries client kinds only, andtern whoamicarries just the identity chain.
Install
pi install git:github.com/Ghost-9/pi-tern
or
pi install npm:pi-tern
Manual install:
git clone https://github.com/Ghost-9/pi-tern ~/.pi/agent/extensions/pi-tern
Reload pi, then check inside a Tern pane:
pi
/reload
/tern doctor
Usage
Commands:
/tern doctor probe result, hello kinds, features, credits
/tern diagnose environment, relay and control diagnostics
/tern run [--last] <command> run a shell command in a visible Tern pane
/tern db <path> <sql> query SQLite through Tern's engine (read-only)
/tern doc <action> <path> read/outline/search/append/write a Tern document
/tern board read <path> read a native Tern board
/tern ask|remember|recall <text> talk to Carly (remote providers; no vault content)
/tern schedule <when> | <title> · tasks · cancel <id>
/tern settings get|list|describe <key>
/tern notebook read <pane> read an open notebook block
/tern notebook run <path> execute a notebook via nbconvert in a visible pane
/tern ui-test <scenario> [expect] shot + control-endpoint assertions
/tern diagram git [dir] render the repository history as a gitGraph
/tern diagram --from "<cmd>" render mermaid printed by a command
/tern mirror search <text> search the session mirror
/tern restore reopen the mirror and the last pinned diagram
/tern control [window|headless] start a control endpoint for tern_ctl
/tern bridge install|refresh|status install the pi-bridge Tern canvas
/tern bridge types ask Tern for its plugin API and check the shipped Luau
/tern whoami the identity chain of this pane (Tern 0.9+)
/tern diagram <mermaid> render a diagram through Tern's merman engine
/tern diagram --last [--pin] render the newest Mermaid fence in the conversation
/tern mirror on|off|open mirror the conversation into a Markdown block
/tern browser {"op":"open","url":"…"}
/tern capture [block] print a pane's visible text
/tern panes list sessions, tabs and blocks as JSON
/tern title | bell apply the title, or ring the bell once
Model-callable tools: tern_status, tern_run, tern_browser are declared; tern_diagram, tern_capture, tern_panes, tern_session, tern_ctl, tern_mirror, tern_watch, tern_diagnose, tern_shot, tern_remote, tern_bridge, tern_db, tern_doc, tern_board, tern_carly, tern_notebook, tern_settings, tern_ui_test are deferred and callable from codemode scripts by name (listed in tern_status).
Examples:
Draw the architecture as a Mermaid diagram and open it in Tern.
Open https://example.com in Tern's browser, snapshot it, click the first link and capture a screenshot.
Start a session mirror so I can read this conversation natively.
Configuration
| Variable | Default | Effect |
|---|---|---|
PI_TERN_PROBE |
1 |
0 skips the TSP hello probe |
PI_TERN_TITLE |
1 |
0 stops title updates |
PI_TERN_BELL |
0 |
1 rings the bell at agent end |
PI_TERN_MIRROR |
0 |
1 starts the session mirror automatically |
PI_TERN_DIAGRAM_AUTO |
0 |
1 auto-opens Mermaid blocks found in replies |
PI_TERN_RELAY |
1 |
0 uses the tern browser CLI instead of the daemon relay |
PI_TERN_RUN |
1 |
0 disables the tern_run tool |
PI_TERN_BRIDGE |
1 |
0 skips the automatic pi-bridge link |
PI_TERN_FORCE |
— | 1 tries Tern features outside a Tern pane |
Prompt footprint
Only three tools are declared to the model — tern_status, tern_run, tern_browser. The other
seventeen are deferred: they do not appear in the prompt and are callable from codemode scripts by
name (the names are listed in tern_status). Measured cost of the whole extension: +774 prompt
tokens (21,704 → 22,478), down from +1,309 in 0.8.0 and +3,679 in 0.7.0. See
docs/BENCHMARKING.md.
How it works
sequenceDiagram
participant P as pi + pi-tern
participant T as Tern pane
P->>T: TSP hello query plus DA1
T-->>P: TSP hello reply (v1, 44 kinds, 10 features, credits 2)
T-->>P: DA1 device attributes
Note over P: probe confirmed, Tern detected
P->>T: tern open diagram.md (merman renders it)
P->>T: daemon relay: hello, welcome, browser op
T-->>P: relay answer ok
Three channels, kept separate:
- pty / TSP — the
helloprobe only. The extension does not open surfaces or send frames. - Tern daemon relay (
$TERN_PANE_SOCKET) — the browser. Frames areu32LE length + UTF-8 JSON; falls back to thetern browserCLI. ternCLI —openfor diagrams and the mirror,capture,ls --json,ctl.
docs/ARCHITECTURE.md has the module map and the probe lifecycle.
Limitations
- Native mode is opt-in and best-effort; the extension half is the supported half. pi-tui's ANSI
frames are multi-write synchronized-output transactions with private cursor accounting, so a second
writer tears them — which is why the extension never writes TSP frames. Native surfaces instead
run through thepi-ternlauncher's loader hook, which depends on pi's internals and is rebased
per pi release. The split is deliberate and documented: extension = stable and supported; native =
opt-in. Outside Tern, in a shell block, or with-p/--mode json|rpc, native mode falls back to
stock pi rather than failing. - Tern's agent layer is omp-keyed.
cx.agents:transcriptreads theomp.sessionsurface and returns{}for non-omp programs, so the Agent chip, Carly transcripts and prompt injection are unavailable. Tern 0.5.1 adds Hermes as a second native agent that speaks omp's chat vocabulary, which suggests this is an implementable contract rather than an omp-only privilege — but the read-side predicate for a third agent is unconfirmed, and impersonatingomp.sessionto reach it is deliberately not attempted. - Tern file blocks are not panes.
tern capturecannot read a file block back (no such pane in this session daemon), so diagram rendering is verified visually. - A session open across a Tern update keeps a stale TSP vocabulary. The daemon used to lag the
binary, which made this look like a platform problem; Tern 0.5.2 fixed that — "the session
daemon switches to the new build as the first window attaches, with your programs still running" —
and verified here: after one window attach a new pane's hello reportedver: "0.5.2"while a
long-running session carried on. So the remedy is attach any window, not restart the daemon.
What remains is ours: the probe runs at session start, so a session that spans an update acts on the
vocabulary it saw then./tern diagnosereports the version it probed with; re-probe after attaching
a new window. - Native surfaces are agent-block-only (see above). In a shell block the pane is blank and nothing
reports an error; 1.1.5 detects it and falls back, earlier versions did not. - Browser capture needs a rendered picture-in-picture. Tern answers
capture: a 0×0 px image is out of rangewhile the PiP is not visible.tern_chartsidesteps this for SVG charts by rasterizing locally (rsvg-convert→inkscape→qlmanage→ browser); for HTML/JS pages the browser is still the only renderer. - Inline figures in the conversation are opt-in and unconfirmed.
PI_TERN_INLINE_IMAGES=1only attempts it; every rejection lands innativeState().lastError. The mechanism is settled: there is noblobframe op — Tern answersunknown op blobfor both["blob",id,mime,data]and["blob",mime,data], and the blob store is a plugin-VM API (blob(self, bytes, mime)), not part of the frame dialect. pi-tern therefore sends the bytes inline in theimagenode, which Tern accepts. The accepted encoding is not the same as seen rendering: use the file block orpng: truefor anything that must work today. Full table indocs/TSP-ENCODINGS.md. - The TSP handshake is a race and is retried. Three attempts with backoff, because on identical
panes one probe received Tern's 633-byte reply immediately while another received nothing across
six retries over 2.4 s. A single 700 ms attempt fails nondeterministically, which is why native
mode used to engage only sometimes./tern diagnosenow reportsprobeFailure—timeout(a
lost race),no-hello-reply(the terminal answered the DA1 sentinel but does not speak TSP), orunexpected-reply:…— instead of failing silently. Tune withPI_TERN_PROBE_ATTEMPTSandPI_TERN_PROBE_TIMEOUT_MS. tern_ctlneeds a control endpoint. Run/tern control(headless by default) or launch Tern with--control EP/ setTERN_WINDOW_SOCKET.- The pi-bridge canvas is experimental. The plugin loads and binds its chord (verified in Tern's log and
plugin list), but the Luau canvas rendering itself has not been visually verified from CI. tern_watch --expectis event-driven. It subscribes once totern eventsrather than starting
atern captureprocess every 500 ms, and falls back to backing-off polls if events do not flow.
The reply reportsvia: "events"orvia: "poll", so the fallback is visible.- Mailbox latency is one plugin poll (~0.25–0.75 s per call); DB access is read-only unless
execis explicitly allowed;agent.db-style stores hold credentials, so pass explicit paths and never select secret columns. - npm 1.1.8 was a partial snapshot and is superseded. A release-workflow bug published it from a
mid-work commit, so the tarball is missing several of this release's files. Install 1.1.9 or
later. The bug is fixed: a tag whose version disagrees withpackage.jsonnow fails the release. - Notebook execution is not exposed by Tern's plugin API;
tern_notebookonly reads open notebook blocks.
Security
pi extensions run inside the pi process with the same operating-system permissions. Read the source before installing, and only install from sources you trust.
- The session mirror is opt-in and writes to
~/.pi/agent/scratch/pi-tern/session-mirror.md. - The browser uses Tern's own WKWebView profile, including its cookies. Treat it as your logged-in browser context.
- The extension sends nothing anywhere; Tern is local.
Report vulnerabilities through the private advisory form.
What is verified, and how
This section exists because of a specific failure. Releases 1.1.0–1.1.4 recorded native surfaces as
"verified" on the strength of frame acceptance — Tern answered with zero errors and the surface
tree came back from capture --surfaces. That method cannot tell a surface that renders from one
that renders nothing, and both statements are equally true of a blank pane. The result was a P0
that shipped: native mode removed pi's entire interface and showed nothing at all.
So the distinction is stated rather than assumed:
| Claim | How it is checked |
|---|---|
| Frame accepted | Tern's own error channel. Strong, but it is acceptance — not display. |
| Layout rendered | Nothing has been verified this way, ever, and it is currently impossible: surfaces display only in an agent block, and on Tern 0.5.1 every harness command that creates one times out after 20 s — for any command. scripts/render-proof.mjs runs on every gate and reports the measured answer (today: BLOCKED); --require makes it a failure. Treat every native render claim as unverified until that probe reports a displayed surface. Full detail and reproduction: docs/RENDER-PROOF.md. |
| Works outside a Tern pane | scripts/verify.ts, 19 live checks run deliberately from outside a pane — the property that makes pi-tern usable from another host. |
| Never breaks stock pi | native/compat.mjs — 7 checks locally, --static (4, no model call) in CI. Differential: the launcher's stdio must be shape-identical to stock pi's, with no TSP frame in either. |
| Types are real | tsc --noEmit under strict, against the actual pi and typebox types. native/ is covered too, and scripts/check-native-types.mjs fails when a hand-written declaration names an export the module lacks. |
Run the whole thing with npm run gate. It prints a count per step and reports skips separately:GATE PASSED (7 run, 0 skip) is not the same claim as GATE PASSED (5 run, 2 skip).
Development
npm install # dev dependencies, including the real pi + typebox types
npm test # the unit suite (96 tests), no Tern required
npm run test:live # relay/browser test; needs a Tern pane
npm run typecheck # strict, against the real pi types
npm run lint
npm run gate # everything, including the live checks (~30 s)
npm run bench # mailbox latency; needs a Tern pane with pi-bridge
The extension itself has no runtime dependencies: Node builtins, pi's APIs and TypeBox schemas
only. pi and typebox are devDependencies, used to typecheck against the real API.
To regenerate the Release pages from a new tag, release.yml now runs the full suite, typecheck,
lint, the declaration check and the static compat tier before publishing to npm, then creates the
Release. Every tag has a Release page, and v1.1.2 carries a warning: it has a data-plane P0, fixed
in v1.1.3.
Credits
- The Tern Surface Protocol is documented by Stencil Labs: https://docs.stencil.so/tern/protocol/. Tern is a Stencil Labs product.
- pi is by Earendil Works.
- The browser relay framing and op vocabulary were informed by oh-my-pi (MIT), the reference TSP client.
- Not affiliated with Stencil Labs or Earendil Works.
License
MIT © 2026 Mayank Batra
Native mode (v1.0.0): run pi-tern instead of pi inside Tern for a native surface — transcript rows in main, a real TSP editor composer (Tern edit/undo/send drive pi), suspend/resume, opt-in surface adopt. Outside Tern or in -p/--mode json|rpc, it runs stock pi. See docs/NATIVE-FINDINGS.md.
Yorumlar (0)
Yorum birakmak icin giris yap.
Yorum birakSonuc bulunamadi