Compatibility
Will June run in your project, and what can your code safely depend on across versions? The table below answers the first; the rest of the page answers the second. For where each export is explained, see Package surfaces.
Requirements
| Requirement | Supported | Notes |
|---|---|---|
| Bun | >=1.2.0 | the runtime for the package and the CLI; nothing else |
react | ^19.3.0 | peer dependency: 19.3 or a later 19.x |
ai | ^7.0.137 | peer dependency: 7.0.137 or a later 7.x |
@ai-sdk/openai | ^4.0.0 | optional peer: only for june/sdk/openai |
@ai-sdk/anthropic | ^4.0.0 | optional peer: only for june/sdk/anthropic |
@ai-sdk/google | ^4.0.0 | optional peer: only for june/sdk/google |
@ai-sdk/xai | ^5.0.0 | optional peer: only for june/sdk/xai |
| Module format | ESM only | import, not require() |
| Models | any AI SDK provider | a provider package that works with your version of ai |
The ranges are the package's own engines and peerDependencies, and a test keeps this table in sync with them.
Bun. The june CLI requires it, and parts of the library call Bun's APIs directly: reading a JSONL file with dataset() or copyCorpus(), trl(), and vertex(). Node, Deno and browsers aren't supported.
ESM only. Every entrypoint is an ES module with import and types conditions and no CommonJS build. The june/sdk/* wrappers also use top-level await to load their provider, so tools that compile ES modules to CommonJS can't load them.
Dependencies. Besides its peers, June depends only on react-reconciler and xstate, at pinned versions. The @ai-sdk/* peers are optional: install the ones whose june/sdk/* wrapper you import. No other entrypoint loads them. Python's ML packages are only ever installed in the TRL runner's isolated environment.
What you can depend on
Three tiers. Stable changes only with a major version, a deprecation period, or a version bump in its own format. Less stable may change in a minor version, with a changelog entry. Not promised can change at any time; don't build on it.
Stable
Entrypoints. june, june/train, june/trainers, june/copy, june/chat, june/tools, june/test and the june/sdk/* provider wrappers, and the values they export, which are locked by a test:
| Entrypoint | Values |
|---|---|
june | Agent, Step, Parallel, Catch, Tool, Bash, Inherit, RequireApproval, Namespace, defineCapabilities, defineAgent, run, start, useStep, useResource, useAgent |
june/train | dataset, fineTune, startFineTune, fineTuneJob, job, JobFailedError, evalSuite, grader, evaluate, compareEvaluations, casesFrom, FineTune, Evaluate, datasetFromSessions, preferenceDatasetFromSessions, evalCasesFromSessions |
june/trainers | trl, vertex |
june/copy | copyCorpus, prepareVoice, PrepareVoiceError, voice, loadVoice, metadataRetriever, stylometry, judge, Write |
june/chat | createChat, modelResponder, agentResponder, memoryStore, fileStore, fullContext, summaryContext, projectChatTrace |
june/tools | bashTool |
june/test | scriptedModel, callTools |
june/sdk/openai | openai |
june/sdk/anthropic | anthropic |
june/sdk/google | google |
june/sdk/xai | xai |
Chat lives only under june/chat; the root june export doesn't re-export it, or any other entrypoint. Modules inside dist/ are not part of the public surface.
Missing providers. Importing a june/sdk/* wrapper without its @ai-sdk/* package throws an error with code: "JUNE_MISSING_PROVIDER_DEPENDENCY" and the fields provider, packageName and importPath. Match on code, not the message. Any other failure while loading a provider is passed on as it is.
Workflow types. The types june exports, such as RunHandle, RunResult, RunSnapshot, TraceNode and StepResult. A compile test checks they still exist.
Run snapshots. snapshot() returns { version: 1, steps }, each step with its cacheKey and result, as described in Snapshots and resume. run() and start() reject a snapshot with any other version rather than guess. Cache keys are opaque, and June doesn't promise the same key for the same step across versions; a key that doesn't match only means that step runs again.
june run --json. When the run succeeds, one JSON object with steps, usage and tree: trace nodes keep their { type, props, children } shape, and step nodes the fields in Traces and usage. New fields may be added. usage, and a step's model and finishReason values, come from the AI SDK and follow the ai version you install; steps and each result are whatever your steps returned. A failed run prints no JSON.
Canonical example formats. completion (prompt, completion), conversation (messages) and preference (input, chosen, rejected), each with optional metadata, and the JSONL reading rules in Datasets.
Fingerprints and splits. A dataset's fingerprint, and which split an example lands in for a given seed, stay the same for the same examples across versions. A change to either algorithm is a breaking change, since it would silently change what a manifest's fingerprint means or leak test examples into training.
The Trainer contract. id, capabilities, start, status and cancel, and the shapes of TrainingRequest, TrainingJob, TrainingStatus and TrainingOutput. New optional request fields may be added; trainers must ignore fields they don't know.
ModelArtifact discriminants. adapter, checkpoint and provider-model, with their documented fields. New artifact types may be added, so handle unknown ones.
Job states. queued, running, succeeded, failed and canceled, with the last three final. JobRef and JobSnapshot shapes.
fineTune()'s result. artifact, metrics, job and manifest, and the manifest's version: 1 format. A breaking change to the manifest bumps its version.
Voice manifests. VoiceManifest with version: 1.
The TRL runner contract. config.json with version: 1, and the status.json and result.json files, as described in Local training, so custom runners keep working.
The chat contracts. Chat, Session, Turn, Responder and ResponseExecution, ChatStore, ContextBuilder, and the ChatEvent and ChatJournalEntry unions. A compile test checks every exported type still exists; their documented fields are stable too. New optional fields and new event types may be added, so handle unknown event types.
Chat journals. Entries are version: 1: session.created, turn.accepted, turn.completed, turn.failed, turn.canceled, turn.steered and feedback.recorded, each with id, sessionId and at. New entry types may be added within version 1, and readers skip types they don't know. A breaking change to an entry bumps version, and June refuses to read entries with a version it doesn't know rather than guess. fileStore()'s layout, one <session id>.jsonl per session, is stable.
The chat CLI. june chat and june ask, --session on both and --json on june ask, and june ask --json's object (sessionId, turnId, text, metadata).
Less stable
These may change in a minor version, with a changelog entry. Where they're output, read their structure, not their text: parse --json, and match typed fields and event types rather than strings.
- Anything under a provider namespace:
providerOptions.vertex,artifact.metadata.vertex,TrainingJob.metadata.trl, and so on. They follow their providers. - Which provider HTTP responses
vertex()retries, and its default retry timing. - The TRL runner's Python dependency versions, and TRL defaults for unset hyperparameters.
- The teacher, writer and judge prompts in
june/copy, and the stylometric features and retrieval weights' defaults. These are tuned as evals show what works; pin your own withinstructions,metadataRetriever(weights)or a custom evaluator if you need them fixed. - The text
june runprints. Use--jsonfor anything that parses output. - The text
june chatprints to stderr, and its slash commands. summaryContext()'s default instructions and the wording of its summary marker.- The
MockLanguageModelV4ajune/testscripted model is, and the call options it records. They come fromai/testand follow theaiversion you install.
Not promised
June makes no promise about these. Code that depends on them will break without warning:
- Generated text: what a model writes, for the same prompt or not.
- Scores from model-based graders and evaluators, such as
judge(), across versions or runs. - Anything a provider decides: which models are tunable, how many examples they need, and what a tuned model costs.
- Modules inside
dist/, and anything else you can't import from an entrypoint. - The same cache key for the same step across June versions.