Little documentation
Guides and APIReference

FileWriter

Writing files into a Little Harness session from hooks, tools, and host code.

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

FileWriter is the common write surface for session files, artifacts, and Persistent Dir harness directories.

The same shape should appear in three places:

WriterWhere it appearsTypical use
fileschat.stageMessageStage uploaded message files before a model turn.
session.filesHost app codeAdd files between turns.
ctx.filesTool handlersWrite large tool outputs without flooding model context.

Basic Usage

await files.write("/session/user-input/msg_123/report.pdf", stream, {
  mediaType: "application/pdf",
  source: "user-message",
});

Tool authors read the injected context, typed, with harnessToolContext(options). Its type parameter types extraBody:

import { harnessToolContext } from "@little-stack/harness";
import { tool } from "ai";
import { z } from "zod";

type RequestContext = {
  userId: string;
};

const queryAccounts = tool({
  description: "Query account rows visible to the current user.",
  inputSchema: z.object({ query: z.string() }),
  execute: async (input, options) => {
    const ctx = harnessToolContext<RequestContext>(options);
    const rows = await accountDb.query({
      query: input.query,
      userId: ctx.extraBody?.userId,
      limit: 500,
    });

    const file = await ctx.files.writeJSON(
      "/artifacts/tool/query-accounts/result.json",
      rows,
    );

    return {
      preview: `${rows.length} rows exported for analysis.`,
      path: file.path,
      bytes: file.bytes,
      artifact: file.artifact,
    };
  },
});

API Shape

type FileWriter = {
  write(path: string, content: FileContent, options?: WriteFileOptions): Promise<FileRef>;
  writeText(path: string, text: string, options?: WriteFileOptions): Promise<FileRef>;
  writeJSON(path: string, value: unknown, options?: WriteFileOptions): Promise<FileRef>;
  read(path: string, options?: ReadFileOptions): Promise<FileData>;
  list(path: string, options?: ListFilesOptions): Promise<FileEntry[]>;
  remove(path: string, options?: RemoveFileOptions): Promise<void>;
};

Methods

write

Writes bytes or a stream.

const file = await files.write("/session/uploads/source.pdf", stream, {
  mediaType: "application/pdf",
});

writeText

Writes UTF-8 text.

const file = await files.writeText(
  "/session/notes/summary.md",
  "# Summary\n\nThe customer asked for renewal options.",
);

writeJSON

Serializes and writes JSON.

const file = await ctx.files.writeJSON(
  "/artifacts/tool/query-customers/result.json",
  rows,
);

read

Reads a file from the session boundary.

const file = await session.files.read("/session/notes/summary.md");

list

Lists files under a directory.

const uploads = await session.files.list("/session/user-input");

remove

Deletes a file or staged path.

await session.files.remove("/session/tmp/old-result.json");

WriteFileOptions

mediaType

string

IANA media type.

source

"user-message" | "app-context" | "tool" | "agent" | string

Origin label for trace events.

metadata

Host metadata object.

Host-facing metadata for traces and debugging.

artifact

boolean | ArtifactOptions

Marks the file as an artifact. Apps can also write directly under /artifacts/.

await ctx.files.writeJSON("/artifacts/report/data.json", data, {
  artifact: true,
});

FileRef

type FileRef = {
  path: string;
  bytes?: number;
  mediaType?: string;
  sha256?: string;
  artifact?: ArtifactRef;
};

Return FileRef values from tools instead of returning large raw data.

return {
  path: file.path,
  bytes: file.bytes,
  summary: "842 customer rows were exported for analysis.",
};

If a tool accidentally returns a very large value, Little Harness can automatically write it through ctx.files under /artifacts/tool-results/ and return a compact file reference to the model. That reference includes line/character counts and, when the value is JSON, a bounded structure sketch.

Path Rules

Little Harness should accept writes only inside managed roots:

RootPurpose
/session/Private workspace for the current session.
/artifacts/Host-inspectable outputs and large generated files.
/persistent/Developer-backed cross-session directories.

Raw host paths should not be exposed by default.

Failure Behavior

If the filesystem write fails, the hook or tool should fail. The data is not available to the agent.

If a Persistent Dir commit fails after the write succeeded, the file remains recoverable inside the session workspace and the failure is surfaced to the host app and trace.

On this page