Little documentation
Guides and APIFoundations

Persistent Dirs

Cross-session files backed by developer-provided storage logic, and how their writes commit.

Little Harness: published on npm as little-harness@0.2.0-alpha.0 (2026-10-05). Sections labeled UNRELEASED describe source-only changes. Release status

Persistent Dirs are opt-in directories whose files can survive beyond one session.

Harness memory is built on this model. memory(...) creates a normal Persistent Dir, lists its files in every turn's prompt, and adds a built-in skill on how the agent keeps them.

They are not global scratch folders. They are developer-backed harness directories: the app decides how to fetch and store files, and Little Harness handles the safe session directory, diffing, tracing, and commit boundary.

persistentDirs: [
  {
    harnessDir: "/persistent/user",
    load: async ({ extraBody }) =>
      appStorage.loadFolder(`users/${extraBody.userId}/memory`),
    store: async ({ extraBody, changes }) =>
      appStorage.applyChanges({
        prefix: `users/${extraBody.userId}/memory`,
        changes,
      }),
  },
]

Load

load runs before the agent uses the Persistent Dir. It returns the files Little Harness exposes at harnessDir for this session.

For simple apps, that can be a folder snapshot. For larger stores, future adapter shapes may load lazily.

Work

The agent edits the harnessDir inside the isolated session. The backing store is not mutated on every write.

preferences.md
acme.md

Mixed Sources

One harness can expose several Persistent Dirs with different backing sources and write policies.

persistentDirs: [
  localDir({
    harnessDir: "/persistent/company",
    sourceDir: projectDir("./docs/company-knowledge"),
    commit: "read-only",
  }),
  localDir({
    harnessDir: "/persistent/user",
    sourceDir: ({ extraBody }) =>
      `persistent/users/${extraBody.userId}/memory`,
    commit: "after-turn",
  }),
]

The read-only company directory rejects writes immediately. The writable user directory accepts changes in the session first, then commits them after the turn.

Little Harness does not infer sharing or read/write policy from names such as company, project, or user. The developer controls the backing source and commit mode for each directory.

Who May Write, Per Turn

commit: "read-only" fixes a directory as read-only for every turn. To decide per turn instead, pass canWrite. It runs when the turn loads the directory, with the turn's extraBody:

persistentDirs: [
  localDir<{ role: string }>({
    harnessDir: "/persistent/company",
    sourceDir: "persistent/company",
    canWrite: ({ extraBody }) => extraBody?.role === "editor",
  }),
]

When it returns false, the directory is read-only for that turn, as with commit: "read-only": writes fail at once and nothing is stored. The load event for the turn carries readOnly: true. Derive the role from your own sign-in, never from what the person typed.

The Commit Model

Persistent Dir writes happen in two phases:

  1. The agent writes files inside the isolated session.
  2. Little Harness commits changes to developer-backed storage at a defined boundary.

The default boundary for agent and tool work is after each agent turn.

After-Turn Commit

An agent turn is one full response to one input, including tool calls, shell commands, generated artifacts, and model output.

persistentDirs: [
  {
    harnessDir: "/persistent/user",
    load,
    store,
    commit: "after-turn",
  },
]

After the turn, the runtime computes a diff and calls store.

Host-Staged Writes

The host can write into a Persistent Dir path without a model turn.

await session.files.write("/persistent/user/uploads/report.pdf", stream);

session.files is a session-workspace writer: the file lands in the isolated session checkout only, and starting the next turn does not commit it โ€” each turn begins by resetting every /persistent/... checkout from backing storage, which discards uncommitted host writes. The one durable host-staging path today is a commit: "manual" dir: write, then call result.commitManual() (on the most recent completed turn's result) before starting another turn. commitManual() skips after-turn dirs, so a host-only write into an after-turn dir never reaches backing storage โ€” have the agent (or a tool) write it during a turn instead, or stage it in a manual-commit dir.

Why Not "Session Done"

"Session done" is not a reliable event. A user can close a tab, a phone can background the app, a serverless function can end, or a network request can disconnect. After-turn commits give a predictable boundary that works for chat, jobs, and APIs.

Commit Failures

Commit failures are host-facing runtime events. They are recorded in the trace and exposed on the result, not injected into the model's normal context. The agent already finished the turn, and the host app is better positioned to retry or warn the user.

const result = streamHarness({ harness, messages, session, extraBody });
const finished = await result.finished;

if (finished.status === "completed" && finished.persistence.status === "failed") {
  // Host app decides whether to retry, warn, or escalate.
  await retryQueue.enqueue(finished.persistence.failedCommits);
}

On this page