npm package: @stonega/pi-expo-agents
Description: Expo AI agent framework with mobile tools for file editing, memory, and compaction
Author: stone
Latest version: 0.54.12

# @stonega/pi-expo-agents

Expo AI agent framework for building intelligent mobile agents with file editing, memory management, and context compaction. Built on top of `expo-file-system` — no Node.js `fs` dependency, safe to bundle on iOS and Android.

## Overview

`pi-expo-agents` provides a self-contained framework for integrating AI agents into Expo mobile applications. It does **not** depend on `@stonega/pi-coding-agent` or any Node.js standard library module.

Key capabilities:

- **Real LLM calls** via `@stonega/pi-ai` (`completeSimple`)
- **Mobile-optimized tools** — read, write, edit, list, remove — backed by `expo-file-system`
- **Memory system** — persistent, token-counted conversation memory
- **Context compaction** — automatic summarization when memory exceeds thresholds
- **Session management** — save and restore named conversation threads
- **Utilities** — system prompt builder, prompt templates, event bus, and text truncation helpers

## Installation

```bash
npm install @stonega/pi-expo-agents expo-file-system
```

`expo-file-system` is a peer dependency and is included in the standard Expo SDK.

## Quick Start

```typescript
import { createExpoAgent, DefaultModelRegistry, buildSystemPrompt } from "@stonega/pi-expo-agents";
import { anthropicModels } from "@stonega/pi-ai";

const registry = new DefaultModelRegistry({
  apiKeys: { anthropic: process.env.EXPO_PUBLIC_ANTHROPIC_API_KEY },
});

const agent = await createExpoAgent({
  model: anthropicModels["claude-3-5-haiku-20241022"],
  modelRegistry: registry,
  systemPrompt: buildSystemPrompt({ appName: "My App" }),
});

const response = await agent.prompt("Hello! What can you help me with?");
console.log(response);
```

## API Reference

### `createExpoAgent(config)`

Factory function — returns a fully initialized `ExpoAgent`.

```typescript
interface ExpoAgentConfig {
  /** Model to use (from @stonega/pi-ai). Required. */
  model: Model<Api>;
  /** Optional registry for API key resolution. */
  modelRegistry?: ModelRegistry;
  /** Optional system prompt string. */
  systemPrompt?: string;
  /** Directory for persisted memory (default: ".expo-agent"). */
  memoryDir?: string;
  /** Directory for sessions (default: "<memoryDir>/sessions"). */
  sessionDir?: string;
  /** Max tokens in memory before compaction (default: 100 000). */
  maxMemoryTokens?: number;
  maxSessionTokens?: number;
  /** Compaction trigger ratio 0–1 (default: 0.85). */
  compactionThreshold?: number;
  /** Enable automatic compaction (default: true). */
  autoCompact?: boolean;
  /** Additional custom tools. */
  customTools?: Tool[];
  /** Override the default ExpoStorage adapter. */
  storageAdapter?: StorageAdapter;
}
```

### `ExpoAgent`

```typescript
class ExpoAgent {
  // Send a message and receive an LLM response
  prompt(message: string): Promise<string>;
  // Add a message to memory directly
  addMessage(role: "user" | "assistant", content: string): Promise<void>;
  // Access current conversation memory
  getMemory(): ExpoAgentMemory;
  // Summarize and compress conversation history
  compactMemory(customInstructions?: string): Promise<void>;
  // Create a new named session
  createSession(name: string): Promise<ExpoAgentSession>;
  // Restore a previously saved session by its ID
  resumeSession(sessionId: string): Promise<ExpoAgentSession>;
}
```

### `DefaultModelRegistry`

A self-contained registry that resolves API keys from explicit values or environment variables.

```typescript
const registry = new DefaultModelRegistry({
  models: [anthropicModels["claude-3-5-haiku-20241022"]],
  apiKeys: { anthropic: "sk-ant-..." },
});

// Register and update at runtime
registry.registerModel(myModel);
registry.setApiKey("openai", "sk-...");
const key = await registry.getApiKey(model);
```

### `ExpoStorage`

Storage adapter backed by `expo-file-system` v2 (class-based API). No `fs` imports.

```typescript
import { ExpoStorage } from "@stonega/pi-expo-agents";
import { Paths, Directory } from "expo-file-system";

// Defaults to Paths.document/documents
const storage = new ExpoStorage();

// Or pass a custom Directory
const storage = new ExpoStorage(new Directory(Paths.document, "my-agent"));

// StorageAdapter interface:
await storage.read("notes/hello.txt");      // string | null
await storage.save("notes/hello.txt", "…"); // void
await storage.remove("notes/hello.txt");    // void
await storage.list("notes");               // string[]
await storage.mkdir("notes/archive");      // void
await storage.exists("notes/hello.txt");   // boolean
```

### `buildSystemPrompt(options)`

Generates a structured system prompt for the agent.

```typescript
import { buildSystemPrompt } from "@stonega/pi-expo-agents";

const system = buildSystemPrompt({
  appName: "Finance Assistant",
  selectedTools: ["read", "write", "edit"],
  appendSystemPrompt: "Always respond in the user's language.",
  contextEntries: [
    { label: "User Profile", content: "Name: Alice, plan: Pro" },
  ],
});
```

### `createExpoTools(storage, baseDir)`

Returns the five built-in file tools (`read`, `write`, `edit`, `list`, `remove`) as `Tool[]` objects compatible with `@stonega/pi-ai`.

```typescript
import { createExpoTools, ExpoStorage } from "@stonega/pi-expo-agents";

const storage = new ExpoStorage();
const tools = createExpoTools(storage, "documents");
```

### Prompt Templates

```typescript
import { expandPromptTemplate, parseCommandArgs, substituteArgs } from "@stonega/pi-expo-agents";

const templates = [{ name: "fix", description: "Fix bug", content: "Fix: $1", source: "builtin" }];

const expanded = expandPromptTemplate("/fix memory leak", templates);
// => "Fix: memory leak"
```

### Event Bus

Lightweight typed event bus for agent lifecycle events.

```typescript
import { createEventBus } from "@stonega/pi-expo-agents";

const { bus, controller } = createEventBus<{ message: string }>();
bus.on("message", (data) => console.log(data));
controller.emit("message", { message: "hello" });
```

### Session Management

```typescript
const session = await agent.createSession("my-session");
await session.prompt("Start a task");
await session.saveSession();

// Later:
const resumed = await agent.resumeSession("session-1234567890");
const response = await resumed.prompt("Continue the task");
```

### Memory Compaction

```typescript
// Manual compaction
await agent.compactMemory("Summarize focusing on key decisions");

// Automatic compaction occurs at compactionThreshold (default 85 % of maxMemoryTokens)
```

## Architecture

```
src/
├── core/
│   ├── agent.ts           # ExpoAgent + createExpoAgent + ExpoAgentSession
│   ├── model-registry.ts  # DefaultModelRegistry
│   ├── memory/            # ExpoAgentMemory
│   └── compaction/        # MemoryCompactor
├── storage/
│   ├── storage.ts         # ExpoStorage (expo-file-system v2)
│   └── types.ts           # StorageAdapter interface
├── tools/
│   ├── read.ts / write.ts / edit.ts / list.ts / remove.ts
│   └── index.ts           # createExpoTools
└── utils/
    ├── system-prompt.ts   # buildSystemPrompt
    ├── prompt-templates.ts
    ├── event-bus.ts
    └── truncate.ts
```

## Development

```bash
npm install
npm run build
npm run test
```

## License

MIT