> ## Documentation Index
> Fetch the complete documentation index at: https://docs.agentium.in/llms.txt
> Use this file to discover all available pages before exploring further.

# Continue a harness conversation

> Two turns, one scoped session, history inspection, and a cross-user isolation check.

Keep a project conversation across sequential runs and verify that another user with the same session ID gets separate state. This recipe uses the runtime's default in-memory session store.

[Download the complete project](/downloads/harness-sessions.zip), extract it, then run `npm install` and `npm run check`. Use `npm start` to execute the recipe with the requirements below.

## Set up the project

Use Node.js `^22.18.0` or `^24.11.0`. In an empty directory:

```bash theme={null}
npm init -y
npm pkg set type=module
npm install @agentium/core@4.0.0 @agentium/harness@4.0.0 openai zod
npm install --save-dev typescript tsx @types/node
export OPENAI_API_KEY="your-key"
```

Running this recipe contacts OpenAI. Set `OPENAI_MODEL` to a compatible model your account can use; see [model choices](/reference/example-models).

## Run two turns

Save this as `harness-sessions.ts`:

```typescript harness-sessions.ts theme={null}
import { pathToFileURL } from "node:url";
import { openai, type ModelProvider } from "@agentium/core";
import { agentDriver, base, HarnessRuntime } from "@agentium/harness";

export async function runConversation(model: ModelProvider) {
  const runtime = new HarnessRuntime({
    definition: base({ id: "project-assistant" }),
    driver: agentDriver({ name: "project-assistant", model, instructions: "Answer briefly from this conversation." }),
    grants: { toolIds: [], modelRoles: ["main"] },
    budgets: { maxModelCalls: 2, maxToolCalls: 0, maxTokens: 4000 },
  });
  const identity = { tenantId: "demo", userId: "alice" };
  const sessionId = "project-42";
  const options = { identity, sessionId };
  try {
    for (const input of ["Our project is called Atlas. Remember that.", "What is our project called?"]) {
      const result = await runtime.run(input, options);
      if (result.status !== "completed") throw new Error(`Conversation ended: ${result.status}`);
      console.log(result.text);
    }
    // Inspect only after the run settles; acquisition is an exclusive lease.
    const lease = await runtime.sessions.acquire(identity, sessionId);
    let historyLength: number;
    try {
      const snapshot = lease.read();
      historyLength = snapshot.history.length;
      console.log("messages:", historyLength, "replayable:", snapshot.replayable);
    } finally {
      lease.release();
    }
    // The same session ID for another user has separate state.
    const other = await runtime.sessions.acquire({ tenantId: "demo", userId: "bob" }, sessionId);
    try {
      if (other.read().history.length !== 0) throw new Error("Unexpected cross-user history");
    } finally {
      other.release();
    }
    return { historyLength };
  } finally {
    const diagnostics = await runtime.resources.closeSession(identity, sessionId);
    if (diagnostics.length) throw new Error(`Session cleanup failed: ${diagnostics.join(", ")}`);
  }
}

if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
  await runConversation(openai(process.env.OPENAI_MODEL ?? "gpt-6-luna"));
}
```

```bash theme={null}
npx tsx harness-sessions.ts
```

The second answer should recall **Atlas**. With ordinary text responses, the stored history contains four messages and is replayable. The check for Bob's history must pass with zero messages.

## Turn this into an application

Scope sessions by `(tenantId, userId, sessionId)`, with tenant and user derived from authentication. Reuse that tuple when the same person continues the conversation. Await the first run before starting the next; the default store permits one writer per scoped session in this process.

Read a snapshot through a lease after execution settles and always release it. `closeSession()` releases resources, not transcript data. The runtime's store is separate from any Agent storage you configure.

## Persistence and recovery

The default store loses data on process exit. To retain conversation state, implement the [session store contract](/harness/sessions) with accurate durability and concurrency guarantees. Persistent history alone does not make an interrupted run resumable. For task recovery and uncertain effects, use [durable execution](/durable/overview).

See [events and controls](/harness/events) for sending a follow-up to an *active* run; that control is different from the sequential settled runs shown here.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.