Little documentation
Guides and APIReference

localHost()

Configure the zero-service local development host.

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

localHost() configures the Local Host used by createHarness().

It is the default implementation target for the first fully working Little Harness SDK: local sessions, local artifacts, local traces, same-session queueing, and local Persistent Dir sources.

Import

import { localHost, localDir, projectDir } from "@little-stack/harness";

Basic Usage

const harness = createHarness({
  host: localHost({ dataDir: ".little-harness" }),
  model,
  system,
  tools,
});

dataDir defaults to .little-harness.

Storage Layout

Local Host stores sessions under dataDir with names that mirror harness paths:

trace.ndjson
status.json

session/ maps to /session, artifacts/ maps to /artifacts, .agents/skills/ contains read-only materialized skills, and persistent/ contains the session checkout for configured Persistent Dirs. workflows/ holds the event stores of workflow runs started by this session's workflow tools (and dynamic-workflows/ those of one-shot plans): localHost() sets the session's dataDir to this folder.

trace.ndjson is append-only newline-delimited JSON. Each line is a validated lh.trace.v2 event. Large trace payloads are stored under /artifacts/trace/... and referenced from events by contentRef.

API Signature

function localHost(options?: LocalHostOptions): HarnessHost;

type LocalHostOptions = {
  dataDir?: string;
  projectRoot?: string;
  maxConcurrentSessions?: number;
  sameSession?: "queue" | "reject";
  sandbox?: HarnessSandboxMode | HarnessSandboxFactory;
  orchestration?: HarnessOrchestrationServices;
};

Parameters

dataDir

string

Local Host storage root. Defaults to .little-harness.

localHost({ dataDir: ".little-harness" })

The host stores session folders, artifacts, traces, local persistent sources, and locks under this directory.

In development, .little-harness is intentionally inspectable. Do not put raw provider credentials or app secrets inside mounted files unless your app's trace redaction policy excludes those paths.

projectRoot

string

Root used to validate projectDir(...). Defaults to the current working directory.

localHost({
  dataDir: ".little-harness",
  projectRoot: process.cwd(),
})

projectDir("../docs") should fail unless it resolves inside projectRoot.

maxConcurrentSessions

number | undefined

Optional cap on simultaneously running sessions.

localHost({ maxConcurrentSessions: 4 })

Same-session work is still serialized even when this allows multiple sessions to run concurrently.

This queue is process-local. It prevents two turns in the same Node.js runtime from mutating a session at once, but it is not a distributed lock across multiple serverless instances or separate processes.

sameSession

"queue" | "reject"

Controls what happens when a second turn targets a session that is already running. Defaults to "queue".

localHost({ sameSession: "queue" })
localHost({ sameSession: "reject" })

The invariant is fixed: one session never runs two turns at the same time.

sandbox

HarnessSandboxMode | HarnessSandboxFactory | undefined, where HarnessSandboxMode is "auto" | "in-process" | "subprocess"

The sandbox the agent's bash tool runs in. Defaults to "auto".

UNRELEASED: published 0.2.0-alpha.0 names this option executionEnvironment (and the types HarnessExecutionEnvironmentMode and HarnessExecutionEnvironmentFactory). See Alpha Migrations.

  • "auto" — in-process just-bash only when the turn's runtime.python and runtime.javascript are both false and network is off; subprocessSandbox() otherwise. Because Python and JavaScript are on unless disabled, most turns get the subprocess sandbox.
  • "in-process" — in-process just-bash for every turn (the pre-0.2 default). A compatibility layer for trusted tool patterns, not an isolation boundary.
  • "subprocess" — subprocessSandbox() with its defaults for every turn.
  • a factory — for example subprocessSandbox({ env, initTimeoutMs }), tieredSandbox({ nextTier }), justBashSandbox(), or your own.
localHost() // "auto"
localHost({ sandbox: "in-process" })
localHost({ sandbox: subprocessSandbox({ watchdogGraceMs: 2_000 }) })

See Execution Environments for the shipped adapters and the custom-factory contract, and Execution Tiers for escalation.

orchestration

HarnessOrchestrationServices | undefined

Durable orchestration ledgers (tasks, continuations, wakeups, workflow queues, results). Defaults to file-backed ledgers under dataDir.

import { localHost } from "@little-stack/harness";
import { createInMemoryOrchestrationServices } from "@little-stack/harness/host";

localHost({ orchestration: createInMemoryOrchestrationServices() });

Custom backends implement the five-method DurableJsonStore and pass it to createDurableOrchestrationServices({ rootDir, store }) — both shipped backends share that same ledger core. HarnessOrchestrationServices, DurableJsonStore and both factories come from @little-stack/harness/host.

Trace Inspection

Local Host traces are designed to be useful from both code and the command line.

From code, use onEvent on the harness or a single run:

const harness = createHarness({
  host: localHost({ dataDir: ".little-harness" }),
  model,
  system,
  tools,
  onEvent: async (event) => {
    await devTraceStore.append(event);
  },
});

From the terminal, the read-only CLI inspects the same Local Host data:

little-harness sessions --data-dir .little-harness
little-harness trace chat_123 --data-dir .little-harness --format pretty
little-harness files chat_123 --data-dir .little-harness
little-harness artifacts chat_123 --data-dir .little-harness
little-harness diff chat_123 --path /persistent/user/notes.md --data-dir .little-harness
little-harness doctor chat_123 --data-dir .little-harness
little-harness outcomes --data-dir .little-harness

These inspection commands do not execute models or tools; the app still owns the model, tools, auth, policy, and runtime configuration.

localDir()

localDir() creates a local filesystem-backed Persistent Dir.

localDir({
  harnessDir: "/persistent/user",
  sourceDir: ({ extraBody }) =>
    `persistent/users/${extraBody.userId}/memory`,
  commit: "after-turn",
});

harnessDir

string

Directory inside the harness filesystem, for example /persistent/user.

This is the path the agent sees.

sourceDir

string | ((context) => string | ProjectDirRef)

Backing directory outside the harness.

Plain relative strings resolve under localHost({ dataDir }).

sourceDir: "persistent/users/u_123/memory"

A function's string result must be a relative path inside dataDir. A result that is absolute, contains .., or resolves to dataDir itself or outside it throws a HarnessPathError, so a request value such as a user id of ../company cannot reach another folder. Validate ids before building the path.

Project files use projectDir(...).

sourceDir: projectDir("./docs")

commit

"after-turn" | "manual" | "read-only"

Commit policy for this Persistent Dir.

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

read-only loads files into the harnessDir but rejects writes to that directory immediately. after-turn buffers session writes and commits them after the turn finishes. manual buffers session writes and skips the automatic commit; call result.commitManual() or finished.commitManual() to commit that completed turn later.

Little Harness does not infer write policy from names like company, project, or user. Developers choose per directory.

projectDir()

projectDir() marks a local source as an intentional project-folder reference.

localDir({
  harnessDir: "/persistent/project-docs",
  sourceDir: projectDir("./docs"),
  commit: "read-only",
});

The resolved path must stay inside projectRoot.

Mixed Persistent Dirs

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

const harness = createHarness({
  host: localHost(),
  model,
  persistentDirs: [
    localDir({
      harnessDir: "/persistent/company",
      sourceDir: ({ extraBody }) =>
        `persistent/companies/${extraBody.companyId}/knowledge`,
      commit: "read-only",
    }),
    localDir({
      harnessDir: "/persistent/user",
      sourceDir: ({ extraBody }) =>
        `persistent/users/${extraBody.userId}/memory`,
      commit: "after-turn",
    }),
  ],
});

The agent sees both directories: /persistent/company/ and /persistent/user/.

The app decides whether a source is company-wide, project-wide, team-wide, or user-specific through extraBody and the adapter's backing source.

Returns

localHost() returns a host object consumed by createHarness().

The exact object can stay opaque. The public contract is that it can create and resume local sessions, enforce same-session serialization, prepare the harness filesystem, record trace files, and commit local Persistent Dirs.

On this page