Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions docs/9-ai-sessions/alternative-providers.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,13 @@ This is useful when:
- You need to keep AI traffic within specific regions for compliance
- You want to consolidate billing through your existing cloud account

## Where to configure

Both providers can be configured at two scopes:

- **Org-scoped (D&R rules and integrations)** — the `bedrock:` / `vertex:` blocks on an `ai_agent` Hive record, or directly on a `SessionRequest`. This page is primarily about that path.
- **User-scoped (per-user BYOK sessions)** — the same provider blocks, posted to `POST /v1/auth/claude/bedrock` and `POST /v1/auth/claude/vertex`. See [User AI Sessions — Step 2: Store Claude Credentials](user-sessions.md#step-2-store-claude-credentials) for the user-side flow. The IAM, region, and model ID guidance in the rest of this page applies to that path as well — only the credential entry mechanism differs.

## Two configuration formats

There are two ways to point a session at a non-Anthropic provider:
Expand Down
146 changes: 146 additions & 0 deletions docs/9-ai-sessions/api-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,7 @@ POST /v1/profiles
"model": "claude-sonnet-4-20250514",
"max_turns": 100,
"max_budget_usd": 10.0,
"system_prompt_suffix": "You are assisting the SOC team. Always cite sensor IDs in findings.",
"mcp_servers": {
"limacharlie": {
"type": "http",
Expand All @@ -272,6 +273,12 @@ POST /v1/profiles
}
```

The `system_prompt_suffix` is free-form text appended to the agent's system prompt for sessions launched from this profile (max 16 KB). Snapshotted onto the session at creation time, so editing the profile later does not retroactively affect already-running sessions.

**Profile-specific Error Responses:**

- `400 system_prompt_suffix_too_long`: the supplied suffix exceeds 16384 bytes (also returned by `PUT /v1/profiles/{profileId}`)

##### Response: 201 Created

```json
Expand Down Expand Up @@ -332,6 +339,73 @@ POST /v1/sessions/{sessionId}/capture-profile

---

### Profile Memory Bank

Each profile can carry a small set of markdown files that get mounted into the runner's `/workspace/.memory/` whenever a session is launched from the profile. See [Profile Memory Bank](profile-memory.md) for an overview and limits.

#### List memory entries

```text
GET /v1/profiles/{profileId}/memories
```

Returns metadata only — bodies are excluded so the response stays small for profiles with many entries.

```json
{
"memories": [
{
"path": "preferences.md",
"size": 412,
"content_hash": "9b2f…",
"created_at": "2026-05-01T10:14:32Z",
"updated_at": "2026-05-04T08:02:11Z"
}
]
}
```

#### Read a memory entry

```text
GET /v1/profiles/{profileId}/memories/content?path=<memory-path>
```

The `path` is URL-encoded; it must satisfy the [naming rules](profile-memory.md#layout-and-limits).

#### Create or update a memory entry

```text
PUT /v1/profiles/{profileId}/memories/content?path=<memory-path>
```

**Request Body:**

```json
{
"content": "## Acme Corp\n- single-tenant deployment\n- compliance: SOC2"
}
```

Returns `201` on insert, `200` on update or no-op (when the supplied content matches the existing body byte-for-byte).

| Failure | Response |
|---|---|
| Invalid path | `400 invalid_memory_path` |
| Body exceeds 64 KiB | `413 memory_content_too_large` |
| Profile already holds 100 entries | `409 max_memories_reached` |
| Profile aggregate would exceed 5 MiB | `409 memory_quota_exceeded` |

#### Delete a memory entry

```text
DELETE /v1/profiles/{profileId}/memories/content?path=<memory-path>
```

Deleting an entry is also done implicitly when its profile is deleted — every entry in the bank is cascaded.

---

### Claude Authentication

#### Start OAuth Flow
Expand Down Expand Up @@ -423,6 +497,76 @@ POST /v1/auth/claude/apikey
}
```

#### Store Bedrock Credentials

```text
POST /v1/auth/claude/bedrock
```

Routes Claude through AWS Bedrock for this user. The body matches the `BedrockConfig` struct used by the org-side `SessionRequest`. Supply either `(access_key_id + secret_access_key)` (with optional `session_token` for STS / SSO temporary credentials) or `bearer_token`. `region` is always required.

**Request Body:**

```json
{
"region": "us-east-1",
"access_key_id": "AKIA...",
"secret_access_key": "...",
"session_token": "...",
"bearer_token": "..."
}
```

##### Response: 200 OK

```json
{
"success": true,
"message": "Bedrock config stored successfully"
}
```

**Error Responses:**

- `400 invalid_bedrock_config`: missing `region`, missing both credential pair and bearer token, mismatched access-key pair, or `session_token` without the access-key pair
- `400 not_registered`: user has not called `POST /v1/register` yet

> Storing Bedrock credentials replaces any previously stored API key, OAuth token, or Vertex config — only one provider is active per user. See [Alternative AI Providers](alternative-providers.md#amazon-bedrock) for IAM, region, and model ID details.

#### Store Vertex Credentials

```text
POST /v1/auth/claude/vertex
```

Routes Claude through Google Cloud Vertex AI for this user. `service_account_json` is the literal contents of the service-account JSON key — the entire downloaded file as a JSON string.

**Request Body:**

```json
{
"project_id": "my-gcp-project",
"region": "us-east5",
"service_account_json": "{\"type\":\"service_account\",\"project_id\":\"...\",\"private_key\":\"...\"}"
}
```

##### Response: 200 OK

```json
{
"success": true,
"message": "Vertex config stored successfully"
}
```

**Error Responses:**

- `400 invalid_vertex_config`: missing `project_id`, `region`, or `service_account_json`
- `400 not_registered`: user has not called `POST /v1/register` yet

> Storing Vertex credentials replaces any previously stored API key, OAuth token, or Bedrock config. The service-account JSON is encrypted at rest and is never returned by the status endpoint. See [Alternative AI Providers](alternative-providers.md#google-cloud-vertex-ai) for GCP-side setup, region selection, and model ID format.

#### Get Credential Status

```text
Expand All @@ -439,6 +583,8 @@ GET /v1/auth/claude/status
}
```

`credential_type` is one of `api_key`, `oauth`, `bedrock`, or `vertex` — whichever provider was most recently stored. Secrets themselves (API keys, OAuth tokens, AWS keys, service-account JSON) are never returned.

#### Delete Credentials

```text
Expand Down
2 changes: 2 additions & 0 deletions docs/9-ai-sessions/memory.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

AI Memory is a per-agent key/value store for content that should outlive a single AI Session. Where [Skills](skills.md) capture *how* an agent works, memory captures *what it has learned* — facts about the environment, prior decisions, ongoing investigations, anything the agent should be able to recall the next time it runs.

> Looking for the per-user equivalent for [User Sessions](user-sessions.md)? See [Profile Memory Bank](profile-memory.md) — a smaller, profile-scoped store that mounts directly into the session workspace.

Each agent owns one record, keyed by an agent identifier you pick. Inside that record, individual memories are addressed by filesystem-style names (`notes/today`, `cases/INC-123/timeline`, `runtime/last-seen-host`, …). Writes are partial: setting a single named memory does not require reading the rest of the record back, and concurrent writes against different memory names on the same agent do not need to coordinate.

## How writes merge
Expand Down
135 changes: 135 additions & 0 deletions docs/9-ai-sessions/profile-memory.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
title: Profile Memory Bank · LimaCharlie AI Sessions

# Profile Memory Bank

User-scoped AI Sessions are not associated with any specific organisation, so they cannot use the org-scoped [`ai_memory` Hive](memory.md). To give Claude durable context across user sessions, each [Session Profile](user-sessions.md#session-profiles) carries a **memory bank**: a small set of markdown files mounted into the runner's workspace at `/workspace/.memory/` whenever a session is launched from that profile.

The memory bank is the user-side analog of `ai_memory`, scoped per profile rather than per org. Use it for things like:

- preferences ("always run terraform plans against `staging` first"),
- recurring projects (a running plan, status notes, decision logs),
- learned facts about your environment (host inventories, compliance constraints, runbook pointers).

Each profile gets its own bank, so you can keep an "investigations" profile, a "reporting" profile, and a "research" profile with independent memories.

## How memories are mounted

When a session starts from a profile that has memories:

1. The session manager loads the bank, decrypts it, and ships it inside the encrypted session config.
2. The runner writes each memory to `/workspace/.memory/<path>` before Claude starts.
3. The runner injects a `## Profile Memory Bank` section into Claude's system prompt that lists the current entry paths and explains the persistence semantics — so the model knows the directory exists, what's in it, and that edits there persist across sessions. Without this section Claude has no reason to look in `.memory/`; the directory does not have any built-in convention in the Agent SDK.
4. While the session runs, the runner watches `/workspace/.memory/`. Whenever Claude writes, edits, or deletes a file there, the change is debounced and synced back to the bank — so anything the model "learns" persists for the next session that uses this profile.

The system-prompt section is only injected for user-based sessions (D&R / org-scoped sessions never carry a profile and never see the bank). For empty banks the section instructs Claude to create the first entry rather than listing entries.

The directory belongs entirely to the bank: do not store transient files in `.memory/`, they will be wiped on the next session start.

## Layout and limits

Memory paths use the same shape as the `ai_memory` Hive:

- relative path, forward slashes only,
- characters limited to `A–Z`, `a–z`, `0–9`, `.`, `_`, `-`, `/`,
- no leading-dot segments (so `.swp`/`~`/`.#…` editor temp files are easy to filter out),
- no `..` traversal,
- maximum depth: 5 segments,
- maximum path length: 256 characters.

Per-profile caps:

| Limit | Value |
|---|---|
| Entries per profile | 100 |
| Bytes per entry | 64 KiB |
| Aggregate bytes per profile | 5 MiB |

Bank entries are stored encrypted at rest using your user-specific key — the same scheme that protects environment variables and MCP server credentials in your profiles.

## Lifecycle

- The memory bank is **decoupled from session lifecycle**. Deleting a session has no effect on its profile's bank.
- The bank is **not part of session suspend archives**. When a session resumes, it remounts the *current* bank — so any edits you make through the API while a session was dormant take effect on resume.
- **Deleting a profile cascades** the deletion to every entry in its bank.

## Managing memories

### REST API

Authenticate with your LimaCharlie JWT (`Authorization: Bearer $LC_JWT`).

#### List entries

Returns metadata only — fetch bodies separately.

```bash
curl https://ai-sessions.limacharlie.io/v1/profiles/$PROFILE_ID/memories \
-H "Authorization: Bearer $LC_JWT"
```

```json
{
"memories": [
{
"path": "preferences.md",
"size": 412,
"content_hash": "9b2f…",
"created_at": "2026-05-01T10:14:32Z",
"updated_at": "2026-05-04T08:02:11Z"
}
]
}
```

#### Read an entry

```bash
curl --get https://ai-sessions.limacharlie.io/v1/profiles/$PROFILE_ID/memories/content \
--data-urlencode "path=projects/acme.md" \
-H "Authorization: Bearer $LC_JWT"
```

#### Create or update an entry

The body is JSON; the markdown content goes inside the `content` field.

```bash
curl -X PUT https://ai-sessions.limacharlie.io/v1/profiles/$PROFILE_ID/memories/content \
-H "Authorization: Bearer $LC_JWT" \
-H "Content-Type: application/json" \
--data-urlencode "path=projects/acme.md" \
-d '{"content": "## Acme Corp\n- single-tenant deployment\n- compliance: SOC2"}'
```

The response includes a `created` flag (true on first insert) and a `changed` flag (false when the supplied content matched the existing entry — in that case the call is a no-op).

#### Delete an entry

```bash
curl -X DELETE --get https://ai-sessions.limacharlie.io/v1/profiles/$PROFILE_ID/memories/content \
--data-urlencode "path=projects/old-deal.md" \
-H "Authorization: Bearer $LC_JWT"
```

### From inside a session

Claude can simply write to `/workspace/.memory/<path>.md` (or read from it) the same way it would any other file. Edits are synced back to the bank automatically — no special API call required from the model.

## Profile memory vs. AI Memory hive

Both stores share a "filesystem-style entry within an owner record" model, but they sit at different scopes:

| | Profile memory bank | [AI Memory Hive](memory.md) |
|---|---|---|
| Scope | One bank per profile (per-user) | One record per agent (per-org) |
| Auth | LC user JWT | LC org/user creds with `ai_memory.{get,set,del}` |
| Mounted into runner | Yes, at `/workspace/.memory/` | No — accessed via `limacharlie ai-memory` CLI |
| Best for | Per-user durable context for interactive sessions | Cross-session agent state for D&R-driven sessions |

If your session has both an LC org binding *and* a profile memory bank, you can use them together: the bank for personal preferences and project notes, the hive for org-scoped operational state.

## Security

- The runner is treated as untrusted code. It cannot make privileged API calls to the session manager.
- Memory writes from inside the runner travel over the same authenticated WebSocket the runner uses to talk to the proxy. The proxy validates path/size limits at the trust boundary and forwards to the session manager, which authoritatively re-derives the (user, profile) target from the persisted session record. A compromised runner cannot redirect writes into another user's profile.
- Bodies are encrypted at rest with your user-specific key.
Loading
Loading