atelier_sdk/lib.rs
1//! The atelier SDK and engine: versioned workspaces humans and AI agents
2//! do real work in, with snapshots, isolated sessions, gated landing, and
3//! a journal that answers who did what and why.
4//!
5//! A [`Workspace`] is a directory whose whole history atelier keeps: every
6//! outstanding edit becomes an attributed [`Snapshot`] before any read
7//! model answers. An actor works in a [`Session`] — its own working copy,
8//! its own change — and lands through the gate: a [`LandingRequest`]
9//! gathers approvals under the workspace's [`LandingPolicy`], then the
10//! apply lands the change on the shared line or parks on a conflict —
11//! never half-applies. Every act becomes a [`JournalEntry`] in the
12//! append-only journal. Diffs ride a fidelity ladder ([`Diff`]): every
13//! file compares at least as bytes, text raises to line diffs, and format
14//! packages — Word documents via `atelier-sdk-docx` — raise to deltas
15//! in the format's own terms. A workspace attaches sources — local
16//! folders, git repositories, bucket prefixes — each mounted with its own
17//! history; landings fan out per source and mirror back.
18//!
19//! The CLI and the MCP and HTTP surfaces are thin shells over this crate:
20//! anything they do, the SDK does directly.
21//!
22//! # Example
23//!
24//! The actor comes from config (`$ATELIER_CONFIG_HOME/config.toml`, else
25//! `$XDG_CONFIG_HOME/atelier/config.toml`, else
26//! `~/.config/atelier/config.toml`):
27//!
28//! ```
29//! use atelier_sdk::{GateOutcome, Instruction, Workspace};
30//!
31//! # #[expect(unsafe_code, reason = "set_var points the lookup at the scratch config")]
32//! # fn set_config_home(home: &std::path::Path) {
33//! # // SAFETY: the doctest runs on this process's only thread.
34//! # unsafe { std::env::set_var("ATELIER_CONFIG_HOME", home) };
35//! # }
36//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
37//! let config = tempfile::tempdir()?;
38//! std::fs::write(
39//! config.path().join("config.toml"),
40//! "[actor]\nname = \"ada\"\nkind = \"human\"\n",
41//! )?;
42//! # set_config_home(config.path());
43//!
44//! // A workspace, a session, one write, and a landing through the gate.
45//! let root = tempfile::tempdir()?;
46//! let mut workspace = Workspace::init(root.path())?;
47//! let actor = workspace.actor().clone();
48//! let session = workspace.open_session(
49//! &actor,
50//! &Instruction {
51//! summary: "draft the notes".to_owned(),
52//! run_ref: None,
53//! verbatim: None,
54//! },
55//! )?;
56//! workspace.session_write(session.id, "notes.md", "The first note.\n")?;
57//! let outcome = workspace.land(session.id)?;
58//! assert!(matches!(outcome, GateOutcome::Landed { .. }));
59//! # Ok(())
60//! # }
61//! ```
62
63mod config;
64mod coordination;
65mod engine;
66mod error;
67mod journal;
68mod landing;
69mod projection;
70mod read;
71mod render;
72mod session;
73mod store;
74mod watch;
75mod workspace;
76
77pub use atelier_sdk_diff::{
78 Address, Confidence, Delta, DeltaKind, Diff, Fidelity, Line, LineKind, PackageId,
79};
80pub use atelier_sdk_remote::is_remote_url;
81pub use config::{
82 Actor, ActorKind, InstructionFidelity, JournalPolicy, LandingPolicy, Source, SourceKind,
83 SyncPolicy,
84};
85pub use error::Error;
86pub use journal::{Act, JournalEntry};
87pub use landing::{
88 Approval, GateOutcome, Landing, LandingRequest, RequestId, RequestState, Restore,
89};
90pub use read::{READ_WINDOW_MAX, ReadResult, ReadWindow};
91pub use render::{printable, render_diff};
92pub use session::{Instruction, Session, SessionId, SessionState, SourceChange};
93pub use watch::{WatchEvent, WatchStop};
94pub use workspace::{PullOutcome, Snapshot, SourceSnapshot, SyncOutcome, Workspace};