---
title: Working directory and $HOME
description: Where the agent works, where the harness keeps its own files, and how to move them.
sidebar:
  order: 3
---

Two directories matter, and they are set in two different places. `workingDirectory` and `sandboxConfig.workDir` decide where the agent works. `$HOME` decides where the harness keeps its own files.

## Where the agent works

`createLocalSandboxSession({ workingDirectory })` resolves the path and creates it with `mkdir -p` if it is missing. That directory becomes the sandbox's `defaultWorkingDirectory`.

What the agent sees is set on the `HarnessAgent`, with `sandboxConfig.workDir`, which is relative to `defaultWorkingDirectory`:

| `workDir` | The agent works in |
| --- | --- |
| `'.'` | `workingDirectory` itself. This is what you want for a repo. |
| omitted | `<workingDirectory>/claude-code-<sessionId>`, a fresh empty subdirectory. Your repo is its parent, not its working directory. |
| `'packages/app'` | `<workingDirectory>/packages/app`, created if missing. |

The omitted case is the default, and the trap: the agent starts without error and finds an empty directory. Always pass `workDir: '.'` when the agent should see your files.

`workDir` must be relative and stay inside `workingDirectory`: `HarnessAgent` throws on an absolute path or one that starts with `..`.

## Where harness state goes

The harness keeps its own files outside the directory the agent works in, under `$HOME/.ai-sdk-harness` of the sandbox. By default the sandbox's `$HOME` is not your real home: it is `ai-sandbox-local/home` under the OS temp directory (`os.tmpdir()`), created if missing.

- /tmp/ai-sandbox-local/home/ (the sandbox's `$HOME`)
  - .ai-sdk-harness/
    - .harness-bootstrap/
      - claude-code/
    - .agent-runs/
- your-repo/

`.harness-bootstrap/claude-code` holds the pinned CLI install and its bridge, and `.agent-runs` holds per-session bridge state. Your real home and your repo stay clean, and the CLI install, which runs `pnpm install`, never runs inside your own pnpm workspace.

The directory is shared by every session and working directory on purpose, so the roughly 20-second first-run CLI install happens once. The OS may clear its temp directory (macOS does after a few days, and Linux often on reboot). That only means a slow first run again.

The harness finds `$HOME` by running `printf "$HOME"` inside the sandbox, so the `HOME` the sandbox gives its processes is what decides all of this.

## Choose it with `homeDirectory`

Pass `homeDirectory` to put the state somewhere else, such as persistent storage you control, or your real home:

```ts
import { homedir } from 'node:os';

const sandboxSession = await createLocalSandboxSession({
	workingDirectory: '/path/to/your/repo',
	homeDirectory: '/path/to/state', // or homedir() to use your real home
});
```

`homeDirectory` is resolved to an absolute path and created with `mkdir -p`. `env.HOME` also works and wins over `homeDirectory`; see [Configuration](/configuration#environment-variables) for the full precedence.

## What a different `$HOME` hides

Everything the agent runs sees this `$HOME`, not only the harness. Anything that reads config from `HOME` will not find your dotfiles under the default:

- Git identity and settings in `~/.gitconfig`, so commits fail or use a wrong author.
- SSH keys and `known_hosts` in `~/.ssh`.
- npm, `gh`, cloud CLI and other tool logins stored under `~`.
- Claude Code's own `~/.claude` and `~/.claude.json`, so the session doesn't see your user-level Claude settings.

Credentials passed through environment variables, such as `ANTHROPIC_API_KEY`, keep working. To get your dotfiles back, pass `homeDirectory: homedir()`, which puts the harness state in your real home. To keep the default and expose only what you need, pass specific variables through `env`, for example `GIT_AUTHOR_NAME`, `GIT_AUTHOR_EMAIL`, or `GIT_CONFIG_GLOBAL` pointing at your `~/.gitconfig`.

### CLI logins still work

The Codex and OpenCode adapters read your existing CLI login on the host, from `$CODEX_HOME` or `~/.codex/auth.json` for Codex and from `~/.local/share/opencode/auth.json` (or `OPENCODE_AUTH_CONTENT`) for OpenCode, then forward it into the sandbox as environment variables, so the temp-dir `HOME` doesn't break them. An API key in the host environment takes precedence over the stored login. OpenCode picks the stored login from its `provider` setting, not from the model id, so for a non-Anthropic model pass it explicitly, as in `createOpenCode({ provider: 'xai' })` with `model: 'xai/grok-4.3'`. Without that, it looks for an Anthropic login and the turn fails with "Model not found". The OpenCode adapter runs OpenCode v1, which reads the v1 auth file, so a login made only with OpenCode v2 isn't seen.
