# Chat

A workflow runs once and finishes. `june/chat` keeps an interaction alive across turns: a session records the conversation, can be reopened later, and stores people's feedback beside the replies it judges.

## Quickstart

Give `createChat` any AI SDK model:

```ts
import { openai } from "june/sdk/openai";
import { createChat } from "june/chat";

const chat = createChat({
  model: openai("gpt-5-mini"),
  instructions: "Be concise.",
});

const session = await chat.createSession();
const turn = session.send("What is June?");

for await (const event of turn) {
  if (event.type === "text-delta") process.stdout.write(event.text);
}

const { text, metadata } = await turn.result;
console.log(
  metadata?.model,
  metadata?.usage?.totalTokens,
  metadata?.finishReason,
);

await session.send("Say that in five words.").result;
```

A chat is configuration and does no IO when you create it. `createSession()` starts a conversation, `send()` starts a turn, and the second turn sees the first. [`src/examples/chat/model.ts`](https://github.com/johno/june/blob/main/packages/june/src/examples/chat/model.ts) runs as-is with an `OPENAI_API_KEY`.

## A persistent chat

Give the chat a `fileStore()` and its sessions survive restarts. A file that default exports a chat also runs in the terminal:

```ts
import { openai } from "june/sdk/openai";
import { createChat, fileStore } from "june/chat";

export default createChat({
  model: openai("gpt-5-mini"),
  instructions: "You are helping build June.",
  store: fileStore(".june/chat"),
});
```

```sh
june chat chat.ts                                  # an interactive chat in your terminal
june ask chat.ts "Explain this package"            # one reply on stdout, for scripts
june ask chat.ts --session <id> "And its tests?"   # continue a saved session
```

The [CLI reference](https://june.dev/docs/cli.md#june-chat) documents both commands. From your own code, reopen the same session with `chat.openSession(id)`: [`src/examples/chat/persistent.ts`](https://github.com/johno/june/blob/main/packages/june/src/examples/chat/persistent.ts) continues a session in a new process. Add `.june/` to `.gitignore`: journals hold every input and reply in plain text. [Sessions and stores](https://june.dev/docs/chat/sessions.md) covers the choice of store.

## How it fits with the rest of June

A turn is answered by a responder, and there are two built in:

- **Model-backed.** `createChat({ model })`, as above, answers each turn with one AI SDK `streamText` call. It only talks: no tools.
- **Workflow-backed.** `agentResponder()` answers each turn by running a June workflow with `start()`. Steps, tools, approvals, `<Catch>`, timeouts and the trace are the runtime's own, unchanged. Chat adds the conversation around the run.

Or write your own; [Responders](https://june.dev/docs/chat/responders.md) helps you choose.

Chat doesn't need anything else, but its journal is also where improving a chat starts. Ratings and preferred edits are stored next to the replies they judge, and `june/train` can read sessions into datasets and eval cases. That's evidence, not a change: nothing about what answers your chat changes until a person evaluates a candidate and promotes it. See [Learning from chat](https://june.dev/docs/chat/learning.md). `june/chat` never depends on `june/train`.

The [Copy Agent](https://github.com/johno/june/blob/main/apps/copy-agent/README.md) is a complete application built this way: each turn runs a [Copy](https://june.dev/docs/copy.md) workflow through `agentResponder()`, people correct replies with preferred edits, and those edits become the evidence a candidate voice is evaluated on.

Chat lives only under `june/chat`; the root `june` export doesn't re-export it.

## Architecture

```text
Chat         reusable configuration: how turns are answered, where sessions are stored
 └ Session   one conversation, with a stable id and a durable journal
    └ Turn   one user input, its execution, and the assistant's reply
       └ Responder   the thing that answers the turn
```

- **Chat**: configuration. A responder (or a model, as shorthand), a store, and a context strategy.
- **Session**: one conversation. Its journal, an append-only log of plain JSON entries, is the source of truth; `turns()` and `messages()` are rebuilt from it.
- **Turn**: one input, answered once. Turns run one at a time, stream live events, and end as completed, failed, canceled or interrupted. A turn is cancelable, and steerable when its responder allows.
- **Responder**: what answers. A model, a June workflow, or anything implementing `respond()`. A workflow turn is one June run; the session outlives every run.
- **ChatStore**: where journals live. Memory, local files, or your database.

```text
            june chat / june ask / your app
                         │ send(input)                  live ChatEvents
                         ▼                              (text-delta, run-update,
┌──────────────────── Session ────────────────────┐      approval-request, …)
│  turns() / messages()  ◀── projection ──┐       │ ───────────────────────▶
│                                         │       │
│  Turn: accepted ─▶ context ─▶ Responder ─▶ outcome
│          │           │           │             │
└──────────┼───────────┼───────────┼─────────────┘
           ▼           │           ▼
   ChatStore journal   │   modelResponder ── AI SDK streamText
   (append-only,       │   agentResponder ── June start() ── <Agent>/<Step>/tools
    canonical)         │   your responder
                       ▼
            fullContext() / summaryContext() / yours
```

Live events are for showing progress and are never stored; journal entries are the few facts worth keeping. Later, and only if you choose, `june/train` reads the journal:

```text
journal ──read by──▶ june/train: datasets, preference pairs, eval cases
```

## What Chat isn't

- **Not a UI framework.** There's no browser UI. `june chat` is a terminal loop; a web or mobile UI is yours to build on the same sessions and events.
- **Not authentication.** June doesn't know who is talking or who may read a session. Your application decides that before it opens one.
- **Not a hosted service or a database.** Sessions live in the store you choose: memory, local files, or your own database.
- **Not exactly-once durable execution.** With a persistent store such as `fileStore()`, an accepted turn's input survives a crash, but its work doesn't resume, and June never retries a turn on its own. A retried turn runs again from the start, tools included.
- **Not a provider registry.** A chat uses whatever model its code builds. Turns are text in and text out, with no attachments.
- **Not hidden memory or retrieval.** The model sees what the context strategy builds from earlier turns in the same session, and nothing else. There's no memory across sessions and no retrieval.
- **Not a second workflow runtime.** A workflow-backed turn is one ordinary June run.

## Guides

1. [Sessions and stores](https://june.dev/docs/chat/sessions.md): create, reopen and persist a conversation; `memoryStore()`, `fileStore()`, your own store; durability
2. [Turns and streaming](https://june.dev/docs/chat/turns.md): a turn from input to settled reply; cancel, steer, failures and retry
3. [Responders](https://june.dev/docs/chat/responders.md): model chats, workflow chats with `agentResponder()`, your own, provenance
4. [Context](https://june.dev/docs/chat/context.md): the transcript versus what a responder sees; `fullContext()` and `summaryContext()`
5. [Feedback and preferred edits](https://june.dev/docs/chat/feedback.md): ratings and edits, recorded as evidence
6. [Learning from chat](https://june.dev/docs/chat/learning.md): from sessions to datasets, eval cases and a promoted candidate
7. [Privacy and security](https://june.dev/docs/chat/privacy.md): what leaves your machine, and what's stored

Runnable examples are in [`src/examples/chat`](https://github.com/johno/june/tree/main/packages/june/src/examples/chat), and the [compatibility](https://june.dev/docs/reference/compatibility.md) page lists which chat contracts are stable.
