Little documentation
Guides and APIReference

Persistent Dir Reference

Proposed callback shapes for developer-backed persistent directories.

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 let the app choose the storage logic while Little Harness owns the harness directory, session isolation, diffing, and commit timing.

Diff-Based Shape

The recommended default is diff-based.

const userMemory = {
  harnessDir: "/persistent/user",

  load: async ({ extraBody }) => {
    return appStorage.loadFolder(`users/${extraBody.userId}/memory`);
  },

  store: async ({ extraBody, changes }) => {
    return appStorage.applyChanges({
      prefix: `users/${extraBody.userId}/memory`,
      changes,
    });
  },
};

Load Result

load can return a map of file paths to content.

{
  "preferences.md": "# Preferences\nPrefer concise briefs.\n",
  "accounts/acme.md": "Known risk: renewal date moved twice.\n"
}

A future version may include metadata such as content type, version, etag, or modified time.

Store Input

store receives changes computed at the commit point.

{
  created: {
    "accounts/contoso.md": "New notes..."
  },
  updated: {
    "preferences.md": "Updated notes..."
  },
  deleted: ["old-note.md"]
}

Commit Modes

commit: "after-turn" // default
commit: "manual"
commit: "read-only"

after-turn should be the default because it is predictable across chat, API, and job-style usage. manual skips the automatic commit and can be committed later from result.commitManual() or finished.commitManual() for the completed turn. read-only exposes files for inspection but rejects writes immediately; it is not a copy-on-write mode.

Per-Turn Write Check

canWrite: ({ extraBody }) => extraBody?.role === "editor"

canWrite runs each turn, before the directory loads. When it returns false (or a promise of false), the directory behaves as read-only for that turn: writes fail at once, store is not called, and the commit result lists the directory as skipped. Without it, every turn may write. localDir({ canWrite }) passes it through.

Conflict Handling

The alpha API does not lock the conflict API yet. A likely shape is for load to return a version cursor and store to receive it back.

load: async () => ({
  files,
  cursor: "etag-or-version",
});

store: async ({ changes, cursor }) => {
  await storage.applyChanges({ changes, ifMatch: cursor });
};

On this page