@neeter/server
Advanced tools
| import type { SessionStore } from "@neeter/types"; | ||
| /** | ||
| * File-based `SessionStore` using append-only JSONL event logs and JSON metadata sidecars. | ||
| * Creates `{dataDir}/sessions/` on first call. Data is written unencrypted — | ||
| * use for development and trusted environments only. | ||
| */ | ||
| export declare function createJsonSessionStore(dataDir: string): SessionStore; |
@@ -6,2 +6,7 @@ import { appendFileSync, existsSync, mkdirSync, readdirSync, readFileSync, unlinkSync, writeFileSync, } from "node:fs"; | ||
| } | ||
| /** | ||
| * File-based `SessionStore` using append-only JSONL event logs and JSON metadata sidecars. | ||
| * Creates `{dataDir}/sessions/` on first call. Data is written unencrypted — | ||
| * use for development and trusted environments only. | ||
| */ | ||
| export function createJsonSessionStore(dataDir) { | ||
@@ -8,0 +13,0 @@ const sessionsDir = join(dataDir, "sessions"); |
| import type { PermissionRequest, PermissionResponse } from "@neeter/types"; | ||
| type RequestListener = (request: PermissionRequest) => void; | ||
| /** | ||
| * Deferred-promise map for browser-side tool approval and user questions. | ||
| * `request()` returns a Promise that blocks the SDK until the user responds | ||
| * via `respond()`. `cancelAll()` denies all pending requests (used on abort). | ||
| */ | ||
| export declare class PermissionGate { | ||
@@ -4,0 +9,0 @@ private pending; |
@@ -0,1 +1,6 @@ | ||
| /** | ||
| * Deferred-promise map for browser-side tool approval and user questions. | ||
| * `request()` returns a Promise that blocks the SDK until the user responds | ||
| * via `respond()`. `cancelAll()` denies all pending requests (used on abort). | ||
| */ | ||
| export class PermissionGate { | ||
@@ -2,0 +7,0 @@ pending = new Map(); |
@@ -0,1 +1,6 @@ | ||
| /** | ||
| * Unbounded async iterable queue. Push values with `push()`, close with `close()`. | ||
| * Consumers `for await` over the channel; they block until a value is available | ||
| * or the channel is closed. | ||
| */ | ||
| export declare class PushChannel<T> implements AsyncIterable<T> { | ||
@@ -2,0 +7,0 @@ private queue; |
@@ -0,1 +1,6 @@ | ||
| /** | ||
| * Unbounded async iterable queue. Push values with `push()`, close with `close()`. | ||
| * Consumers `for await` over the channel; they block until a value is available | ||
| * or the channel is closed. | ||
| */ | ||
| export class PushChannel { | ||
@@ -2,0 +7,0 @@ queue = []; |
+5
-0
| import { Hono } from "hono"; | ||
| import { type SessionManager } from "./session.js"; | ||
| import { type MessageTranslator } from "./translator.js"; | ||
| /** | ||
| * Returns a Hono app with eight routes for session management, SSE streaming, | ||
| * permissions, and abort. Mounts under `basePath` (default: `"/api"`). | ||
| */ | ||
| export declare function createAgentRouter<TCtx>(config: { | ||
| sessions: SessionManager<TCtx>; | ||
| translator: MessageTranslator<TCtx>; | ||
| /** URL prefix for all routes. Defaults to `"/api"`. */ | ||
| basePath?: string; | ||
| }): Hono; |
+4
-0
@@ -16,2 +16,6 @@ import { Hono } from "hono"; | ||
| ]); | ||
| /** | ||
| * Returns a Hono app with eight routes for session management, SSE streaming, | ||
| * permissions, and abort. Mounts under `basePath` (default: `"/api"`). | ||
| */ | ||
| export function createAgentRouter(config) { | ||
@@ -18,0 +22,0 @@ const { sessions, translator, basePath = "/api" } = config; |
+18
-0
@@ -5,13 +5,22 @@ import { type HookCallbackMatcher, type HookEvent, query } from "@anthropic-ai/claude-agent-sdk"; | ||
| type SDKMessage = ReturnType<typeof query> extends AsyncGenerator<infer T> ? T : never; | ||
| /** Configuration returned by the `SessionManager` factory for each new session. */ | ||
| export interface SessionInit<TCtx> { | ||
| /** Per-session application state, accessible in translator hooks. */ | ||
| context: TCtx; | ||
| /** Claude model ID (e.g. `"claude-sonnet-4-5-20250929"`). */ | ||
| model: string; | ||
| systemPrompt: string; | ||
| /** MCP servers keyed by name — the name becomes the middle segment of tool names (`mcp__{name}__{tool}`). */ | ||
| mcpServers?: Record<string, unknown>; | ||
| tools?: unknown[]; | ||
| /** Glob patterns for allowed tools (e.g. `["mcp__myServer__*"]`). */ | ||
| allowedTools?: string[]; | ||
| disallowedTools?: string[]; | ||
| /** Maximum SDK turns before the session stops. Defaults to 200. */ | ||
| maxTurns?: number; | ||
| /** Working directory for file-based tools. */ | ||
| cwd?: string; | ||
| /** Controls browser-side tool approval. Defaults to `"bypassPermissions"`. */ | ||
| permissionMode?: "default" | "acceptEdits" | "plan" | "bypassPermissions"; | ||
| /** Enable extended thinking (chain-of-thought). Off by default. */ | ||
| thinking?: { | ||
@@ -23,2 +32,3 @@ type: "enabled"; | ||
| }; | ||
| /** SDK lifecycle hooks (e.g. `PreToolUse` for sandbox enforcement via `createSandboxHook`). */ | ||
| hooks?: Partial<Record<HookEvent, HookCallbackMatcher[]>>; | ||
@@ -48,2 +58,10 @@ } | ||
| } | ||
| /** | ||
| * Manages agent sessions — create, resume, look up, and clean up. | ||
| * | ||
| * The `factory` callback runs once per session and returns a `SessionInit`. | ||
| * When resuming, the original session is passed so you can carry context forward. | ||
| * | ||
| * Pass `options.store` to persist session history and event logs across restarts. | ||
| */ | ||
| export declare class SessionManager<TCtx> { | ||
@@ -50,0 +68,0 @@ private sessions; |
+8
-0
@@ -21,2 +21,10 @@ import { query, } from "@anthropic-ai/claude-agent-sdk"; | ||
| const DEFAULT_IDLE_TIMEOUT_MS = 30 * 60 * 1000; | ||
| /** | ||
| * Manages agent sessions — create, resume, look up, and clean up. | ||
| * | ||
| * The `factory` callback runs once per session and returns a `SessionInit`. | ||
| * When resuming, the original session is passed so you can carry context forward. | ||
| * | ||
| * Pass `options.store` to persist session history and event logs across restarts. | ||
| */ | ||
| export class SessionManager { | ||
@@ -23,0 +31,0 @@ sessions = new Map(); |
| import type { CustomEvent, SSEEvent } from "@neeter/types"; | ||
| import type { Session } from "./session.js"; | ||
| export interface TranslatorConfig<TCtx> { | ||
| /** Inspect completed tool results and optionally return custom events to send to the client. */ | ||
| onToolResult?: (toolName: string, result: string, session: Session<TCtx>) => CustomEvent[]; | ||
| } | ||
| /** Converts raw Claude Agent SDK messages into semantically named SSE events. */ | ||
| export declare class MessageTranslator<TCtx> { | ||
@@ -14,3 +16,9 @@ private config; | ||
| } | ||
| /** Formats an SSEEvent as an `event: ...\ndata: ...\n\n` string for the wire. */ | ||
| export declare function sseEncode(evt: SSEEvent): string; | ||
| /** | ||
| * Drives the SDK message loop and yields translated SSE events. | ||
| * The optional `onEvent` callback fires for each event — use it | ||
| * to persist events to a `SessionStore`. | ||
| */ | ||
| export declare function streamSession<TCtx>(session: Session<TCtx>, translator: MessageTranslator<TCtx>, onEvent?: (evt: SSEEvent) => void): AsyncGenerator<SSEEvent>; |
| import { PushChannel } from "./push-channel.js"; | ||
| /** Converts raw Claude Agent SDK messages into semantically named SSE events. */ | ||
| export class MessageTranslator { | ||
@@ -207,5 +208,11 @@ config; | ||
| } | ||
| /** Formats an SSEEvent as an `event: ...\ndata: ...\n\n` string for the wire. */ | ||
| export function sseEncode(evt) { | ||
| return `event: ${evt.event}\ndata: ${evt.data}\n\n`; | ||
| } | ||
| /** | ||
| * Drives the SDK message loop and yields translated SSE events. | ||
| * The optional `onEvent` callback fires for each event — use it | ||
| * to persist events to a `SessionStore`. | ||
| */ | ||
| export async function* streamSession(session, translator, onEvent) { | ||
@@ -212,0 +219,0 @@ const output = new PushChannel(); |
+2
-2
| { | ||
| "name": "@neeter/server", | ||
| "version": "0.10.0", | ||
| "version": "0.10.1", | ||
| "description": "Hono server toolkit for building chat UIs on top of the Claude Agent SDK", | ||
@@ -24,3 +24,3 @@ "license": "MIT", | ||
| "dependencies": { | ||
| "@neeter/types": "0.10.0" | ||
| "@neeter/types": "0.10.1" | ||
| }, | ||
@@ -27,0 +27,0 @@ "peerDependencies": { |
+2
-25
@@ -48,26 +48,2 @@ # @neeter/server | ||
| This gives you eight endpoints: | ||
| | Method | Path | Description | | ||
| |--------|------|-------------| | ||
| | `POST` | `/api/sessions` | Create a session | | ||
| | `POST` | `/api/sessions/resume` | Resume or fork a session by SDK session ID | | ||
| | `GET` | `/api/sessions/history` | List previous sessions | | ||
| | `GET` | `/api/sessions/replay/:sdkSessionId` | Load persisted events for UI replay | | ||
| | `POST` | `/api/sessions/:id/messages` | Send a message | | ||
| | `GET` | `/api/sessions/:id/events` | SSE event stream | | ||
| | `POST` | `/api/sessions/:id/permissions` | Respond to a permission request | | ||
| | `POST` | `/api/sessions/:id/abort` | Abort the current turn | | ||
| ## Key features | ||
| - **Multi-turn sessions** — `SessionManager` + `PushChannel` let users send messages at any time, even while the agent is running. | ||
| - **Named SSE events** — `MessageTranslator` reshapes the SDK's flat message stream into `text_delta`, `tool_start`, `tool_call`, `tool_result`, and more. | ||
| - **Tool result hooks** — `onToolResult` lets you inspect what the agent did and emit structured custom events. | ||
| - **Permission modes** — `bypassPermissions`, `default`, `acceptEdits`, or `plan` — with browser-side approval via `PermissionGate`. | ||
| - **Extended thinking** — Pass `thinking: { type: "enabled", budgetTokens: N }` to stream chain-of-thought reasoning. | ||
| - **Session resume & persistence** — Resume past conversations with `SessionManager.resume()`. Opt into persistence with `createJsonSessionStore` for history and event replay that survive server restarts. | ||
| - **Abort** — Cancel the current agent turn mid-stream. | ||
| - **Sandbox hooks** — `createSandboxHook` restricts file operations to a directory. | ||
| ## Examples | ||
@@ -82,3 +58,4 @@ | ||
| See the [neeter README](https://github.com/quantumleeps/neeter#readme) for full API reference, session context examples, and permission configuration. | ||
| - [Server Guide](https://github.com/quantumleeps/neeter/blob/main/docs/server.md) — endpoints, permissions, persistence, session context, sandbox hooks | ||
| - [API Reference](https://github.com/quantumleeps/neeter/blob/main/docs/api-reference.md) — all exports and types | ||
@@ -85,0 +62,0 @@ ## License |
44604
5.73%1005
8.65%63
-26.74%+ Added
- Removed
Updated