chore: fix bad merge

This commit is contained in:
Nathan Flurry 2026-02-11 07:52:48 -08:00
parent 1dd45908a3
commit 94353f7696
205 changed files with 19244 additions and 14866 deletions

File diff suppressed because it is too large Load diff

View file

@ -5,36 +5,57 @@
export interface paths {
"/v2/fs/file": {
get: operations["get_v2_fs_file"];
put: operations["put_v2_fs_file"];
"/v1/acp": {
get: operations["get_v1_acp_servers"];
};
"/v2/fs/upload-batch": {
post: operations["post_v2_fs_upload_batch"];
"/v1/acp/{server_id}": {
get: operations["get_v1_acp"];
post: operations["post_v1_acp"];
delete: operations["delete_v1_acp"];
};
"/v2/health": {
/**
* v2 Health
* @description Returns server health for the v2 ACP surface.
*/
get: operations["get_v2_health"];
"/v1/agents": {
get: operations["get_v1_agents"];
};
"/v2/rpc": {
/**
* ACP SSE
* @description Streams ACP JSON-RPC envelopes for an ACP client over SSE.
*/
get: operations["get_v2_acp"];
/**
* ACP POST
* @description Sends ACP JSON-RPC envelopes to an ACP client and returns request responses.
*/
post: operations["post_v2_acp"];
/**
* ACP Close
* @description Closes an ACP client and releases agent process resources.
*/
delete: operations["delete_v2_acp"];
"/v1/agents/{agent}": {
get: operations["get_v1_agent"];
};
"/v1/agents/{agent}/install": {
post: operations["post_v1_agent_install"];
};
"/v1/config/mcp": {
get: operations["get_v1_config_mcp"];
put: operations["put_v1_config_mcp"];
delete: operations["delete_v1_config_mcp"];
};
"/v1/config/skills": {
get: operations["get_v1_config_skills"];
put: operations["put_v1_config_skills"];
delete: operations["delete_v1_config_skills"];
};
"/v1/fs/entries": {
get: operations["get_v1_fs_entries"];
};
"/v1/fs/entry": {
delete: operations["delete_v1_fs_entry"];
};
"/v1/fs/file": {
get: operations["get_v1_fs_file"];
put: operations["put_v1_fs_file"];
};
"/v1/fs/mkdir": {
post: operations["post_v1_fs_mkdir"];
};
"/v1/fs/move": {
post: operations["post_v1_fs_move"];
};
"/v1/fs/stat": {
get: operations["get_v1_fs_stat"];
};
"/v1/fs/upload-batch": {
post: operations["post_v1_fs_upload_batch"];
};
"/v1/health": {
get: operations["get_v1_health"];
};
}
@ -50,6 +71,18 @@ export interface components {
params?: unknown;
result?: unknown;
};
AcpPostQuery: {
agent?: string | null;
};
AcpServerInfo: {
agent: string;
/** Format: int64 */
createdAtMs: number;
serverId: string;
};
AcpServerListResponse: {
servers: components["schemas"]["AcpServerInfo"][];
};
AgentCapabilities: {
commandExecution: boolean;
errorEvents: boolean;
@ -72,12 +105,11 @@ export interface components {
};
AgentInfo: {
capabilities: components["schemas"]["AgentCapabilities"];
configError?: string | null;
configOptions?: unknown[] | null;
credentialsAvailable: boolean;
defaultModel?: string | null;
id: string;
installed: boolean;
models?: components["schemas"]["AgentModelInfo"][] | null;
modes?: components["schemas"]["AgentModeInfo"][] | null;
path?: string | null;
serverStatus?: components["schemas"]["ServerStatusInfo"] | null;
version?: string | null;
@ -100,28 +132,17 @@ export interface components {
AgentListResponse: {
agents: components["schemas"]["AgentInfo"][];
};
AgentModeInfo: {
description: string;
id: string;
name: string;
};
AgentModelInfo: {
id: string;
name?: string | null;
};
/** @enum {string} */
ErrorType: "invalid_request" | "unsupported_agent" | "agent_not_installed" | "install_failed" | "agent_process_exited" | "token_invalid" | "permission_denied" | "session_not_found" | "session_already_exists" | "mode_not_supported" | "stream_error" | "timeout";
ErrorType: "invalid_request" | "conflict" | "unsupported_agent" | "agent_not_installed" | "install_failed" | "agent_process_exited" | "token_invalid" | "permission_denied" | "not_acceptable" | "unsupported_media_type" | "session_not_found" | "session_already_exists" | "mode_not_supported" | "stream_error" | "timeout";
FsActionResponse: {
path: string;
};
FsDeleteQuery: {
path: string;
recursive?: boolean | null;
sessionId?: string | null;
};
FsEntriesQuery: {
path?: string | null;
sessionId?: string | null;
};
FsEntry: {
entryType: components["schemas"]["FsEntryType"];
@ -144,10 +165,6 @@ export interface components {
};
FsPathQuery: {
path: string;
sessionId?: string | null;
};
FsSessionQuery: {
sessionId?: string | null;
};
FsStat: {
entryType: components["schemas"]["FsEntryType"];
@ -158,7 +175,6 @@ export interface components {
};
FsUploadBatchQuery: {
path?: string | null;
sessionId?: string | null;
};
FsUploadBatchResponse: {
paths: string[];
@ -172,6 +188,39 @@ export interface components {
HealthResponse: {
status: string;
};
McpConfigQuery: {
directory: string;
mcpName: string;
};
McpServerConfig: ({
args?: string[];
command: string;
cwd?: string | null;
enabled?: boolean | null;
env?: {
[key: string]: string;
} | null;
/** Format: int64 */
timeoutMs?: number | null;
/** @enum {string} */
type: "local";
}) | ({
bearerTokenEnvVar?: string | null;
enabled?: boolean | null;
envHeaders?: {
[key: string]: string;
} | null;
headers?: {
[key: string]: string;
} | null;
oauth?: Record<string, unknown> | null | null;
/** Format: int64 */
timeoutMs?: number | null;
transport?: string | null;
/** @enum {string} */
type: "remote";
url: string;
});
ProblemDetails: {
detail?: string | null;
instance?: string | null;
@ -182,50 +231,25 @@ export interface components {
[key: string]: unknown;
};
/** @enum {string} */
ServerStatus: "running" | "stopped" | "error";
ServerStatus: "running" | "stopped";
ServerStatusInfo: {
baseUrl?: string | null;
lastError?: string | null;
/** Format: int64 */
restartCount: number;
status: components["schemas"]["ServerStatus"];
/** Format: int64 */
uptimeMs?: number | null;
};
SessionInfo: {
agent: string;
agentMode: string;
/** Format: int64 */
createdAt: number;
directory?: string | null;
ended: boolean;
/** Format: int64 */
eventCount: number;
model?: string | null;
nativeSessionId?: string | null;
permissionMode: string;
sessionId: string;
terminationInfo?: components["schemas"]["TerminationInfo"] | null;
title?: string | null;
/** Format: int64 */
updatedAt: number;
SkillSource: {
ref?: string | null;
skills?: string[] | null;
source: string;
subpath?: string | null;
type: string;
};
SessionListResponse: {
sessions: components["schemas"]["SessionInfo"][];
SkillsConfig: {
sources: components["schemas"]["SkillSource"][];
};
StderrOutput: {
head?: string | null;
tail?: string | null;
totalLines?: number | null;
truncated: boolean;
};
TerminationInfo: {
/** Format: int32 */
exitCode?: number | null;
message?: string | null;
reason: string;
stderr?: components["schemas"]["StderrOutput"] | null;
terminatedBy: string;
SkillsConfigQuery: {
directory: string;
skillName: string;
};
};
responses: never;
@ -241,89 +265,23 @@ export type external = Record<string, never>;
export interface operations {
get_v2_fs_file: {
parameters: {
query: {
/** @description File path */
path: string;
/** @description Session id for relative path base */
session_id?: string | null;
};
};
get_v1_acp_servers: {
responses: {
/** @description File content */
200: {
content: never;
};
};
};
put_v2_fs_file: {
parameters: {
query: {
/** @description File path */
path: string;
/** @description Session id for relative path base */
session_id?: string | null;
};
};
/** @description Raw file bytes */
requestBody: {
content: {
"text/plain": string;
};
};
responses: {
/** @description Write result */
/** @description Active ACP server instances */
200: {
content: {
"application/json": components["schemas"]["FsWriteResponse"];
"application/json": components["schemas"]["AcpServerListResponse"];
};
};
};
};
post_v2_fs_upload_batch: {
get_v1_acp: {
parameters: {
query?: {
/** @description Destination path */
path?: string | null;
/** @description Session id for relative path base */
session_id?: string | null;
path: {
/** @description Client-defined ACP server id */
server_id: string;
};
};
/** @description tar archive body */
requestBody: {
content: {
"text/plain": string;
};
};
responses: {
/** @description Upload/extract result */
200: {
content: {
"application/json": components["schemas"]["FsUploadBatchResponse"];
};
};
};
};
/**
* v2 Health
* @description Returns server health for the v2 ACP surface.
*/
get_v2_health: {
responses: {
/** @description Service health response */
200: {
content: {
"application/json": components["schemas"]["HealthResponse"];
};
};
};
};
/**
* ACP SSE
* @description Streams ACP JSON-RPC envelopes for an ACP client over SSE.
*/
get_v2_acp: {
responses: {
/** @description SSE stream of ACP envelopes */
200: {
@ -335,19 +293,31 @@ export interface operations {
"application/json": components["schemas"]["ProblemDetails"];
};
};
/** @description Unknown ACP client */
/** @description Unknown ACP server */
404: {
content: {
"application/json": components["schemas"]["ProblemDetails"];
};
};
/** @description Client does not accept SSE responses */
406: {
content: {
"application/json": components["schemas"]["ProblemDetails"];
};
};
};
};
/**
* ACP POST
* @description Sends ACP JSON-RPC envelopes to an ACP client and returns request responses.
*/
post_v2_acp: {
post_v1_acp: {
parameters: {
query?: {
/** @description Agent id required for first POST */
agent?: string | null;
};
path: {
/** @description Client-defined ACP server id */
server_id: string;
};
};
requestBody: {
content: {
"application/json": components["schemas"]["AcpEnvelope"];
@ -370,12 +340,30 @@ export interface operations {
"application/json": components["schemas"]["ProblemDetails"];
};
};
/** @description Unknown ACP client */
/** @description Unknown ACP server */
404: {
content: {
"application/json": components["schemas"]["ProblemDetails"];
};
};
/** @description Client does not accept JSON responses */
406: {
content: {
"application/json": components["schemas"]["ProblemDetails"];
};
};
/** @description ACP server bound to different agent */
409: {
content: {
"application/json": components["schemas"]["ProblemDetails"];
};
};
/** @description Unsupported media type */
415: {
content: {
"application/json": components["schemas"]["ProblemDetails"];
};
};
/** @description ACP agent process response timeout */
504: {
content: {
@ -384,23 +372,128 @@ export interface operations {
};
};
};
/**
* ACP Close
* @description Closes an ACP client and releases agent process resources.
*/
delete_v2_acp: {
delete_v1_acp: {
parameters: {
path: {
/** @description Client-defined ACP server id */
server_id: string;
};
};
responses: {
/** @description ACP client closed */
/** @description ACP server closed */
204: {
content: never;
};
};
};
get_v1_agents: {
parameters: {
query?: {
/** @description When true, include version/path/configOptions (slower) */
config?: boolean | null;
/** @description When true, bypass version cache */
no_cache?: boolean | null;
};
};
responses: {
/** @description List of v1 agents */
200: {
content: {
"application/json": components["schemas"]["AgentListResponse"];
};
};
/** @description Authentication required */
401: {
content: {
"application/json": components["schemas"]["ProblemDetails"];
};
};
};
};
get_v1_agent: {
parameters: {
query?: {
/** @description When true, include version/path/configOptions (slower) */
config?: boolean | null;
/** @description When true, bypass version cache */
no_cache?: boolean | null;
};
path: {
/** @description Agent id */
agent: string;
};
};
responses: {
/** @description Agent info */
200: {
content: {
"application/json": components["schemas"]["AgentInfo"];
};
};
/** @description Unknown agent */
400: {
content: {
"application/json": components["schemas"]["ProblemDetails"];
};
};
/** @description Authentication required */
401: {
content: {
"application/json": components["schemas"]["ProblemDetails"];
};
};
};
};
post_v1_agent_install: {
parameters: {
path: {
/** @description Agent id */
agent: string;
};
};
requestBody: {
content: {
"application/json": components["schemas"]["AgentInstallRequest"];
};
};
responses: {
/** @description Agent install result */
200: {
content: {
"application/json": components["schemas"]["AgentInstallResponse"];
};
};
/** @description Invalid request */
400: {
content: {
"application/json": components["schemas"]["ProblemDetails"];
};
};
/** @description Unknown ACP client */
/** @description Install failed */
500: {
content: {
"application/json": components["schemas"]["ProblemDetails"];
};
};
};
};
get_v1_config_mcp: {
parameters: {
query: {
/** @description Target directory */
directory: string;
/** @description MCP entry name */
mcpName: string;
};
};
responses: {
/** @description MCP entry */
200: {
content: {
"application/json": components["schemas"]["McpServerConfig"];
};
};
/** @description Entry not found */
404: {
content: {
"application/json": components["schemas"]["ProblemDetails"];
@ -408,4 +501,251 @@ export interface operations {
};
};
};
put_v1_config_mcp: {
parameters: {
query: {
/** @description Target directory */
directory: string;
/** @description MCP entry name */
mcpName: string;
};
};
requestBody: {
content: {
"application/json": components["schemas"]["McpServerConfig"];
};
};
responses: {
/** @description Stored */
204: {
content: never;
};
};
};
delete_v1_config_mcp: {
parameters: {
query: {
/** @description Target directory */
directory: string;
/** @description MCP entry name */
mcpName: string;
};
};
responses: {
/** @description Deleted */
204: {
content: never;
};
};
};
get_v1_config_skills: {
parameters: {
query: {
/** @description Target directory */
directory: string;
/** @description Skill entry name */
skillName: string;
};
};
responses: {
/** @description Skills entry */
200: {
content: {
"application/json": components["schemas"]["SkillsConfig"];
};
};
/** @description Entry not found */
404: {
content: {
"application/json": components["schemas"]["ProblemDetails"];
};
};
};
};
put_v1_config_skills: {
parameters: {
query: {
/** @description Target directory */
directory: string;
/** @description Skill entry name */
skillName: string;
};
};
requestBody: {
content: {
"application/json": components["schemas"]["SkillsConfig"];
};
};
responses: {
/** @description Stored */
204: {
content: never;
};
};
};
delete_v1_config_skills: {
parameters: {
query: {
/** @description Target directory */
directory: string;
/** @description Skill entry name */
skillName: string;
};
};
responses: {
/** @description Deleted */
204: {
content: never;
};
};
};
get_v1_fs_entries: {
parameters: {
query?: {
/** @description Directory path */
path?: string | null;
};
};
responses: {
/** @description Directory entries */
200: {
content: {
"application/json": components["schemas"]["FsEntry"][];
};
};
};
};
delete_v1_fs_entry: {
parameters: {
query: {
/** @description File or directory path */
path: string;
/** @description Delete directory recursively */
recursive?: boolean | null;
};
};
responses: {
/** @description Delete result */
200: {
content: {
"application/json": components["schemas"]["FsActionResponse"];
};
};
};
};
get_v1_fs_file: {
parameters: {
query: {
/** @description File path */
path: string;
};
};
responses: {
/** @description File content */
200: {
content: never;
};
};
};
put_v1_fs_file: {
parameters: {
query: {
/** @description File path */
path: string;
};
};
/** @description Raw file bytes */
requestBody: {
content: {
"text/plain": string;
};
};
responses: {
/** @description Write result */
200: {
content: {
"application/json": components["schemas"]["FsWriteResponse"];
};
};
};
};
post_v1_fs_mkdir: {
parameters: {
query: {
/** @description Directory path */
path: string;
};
};
responses: {
/** @description Directory created */
200: {
content: {
"application/json": components["schemas"]["FsActionResponse"];
};
};
};
};
post_v1_fs_move: {
requestBody: {
content: {
"application/json": components["schemas"]["FsMoveRequest"];
};
};
responses: {
/** @description Move result */
200: {
content: {
"application/json": components["schemas"]["FsMoveResponse"];
};
};
};
};
get_v1_fs_stat: {
parameters: {
query: {
/** @description Path to stat */
path: string;
};
};
responses: {
/** @description Path metadata */
200: {
content: {
"application/json": components["schemas"]["FsStat"];
};
};
};
};
post_v1_fs_upload_batch: {
parameters: {
query?: {
/** @description Destination path */
path?: string | null;
};
};
/** @description tar archive body */
requestBody: {
content: {
"text/plain": string;
};
};
responses: {
/** @description Upload/extract result */
200: {
content: {
"application/json": components["schemas"]["FsUploadBatchResponse"];
};
};
};
};
get_v1_health: {
responses: {
/** @description Service health response */
200: {
content: {
"application/json": components["schemas"]["HealthResponse"];
};
};
};
};
}

View file

@ -1,45 +1,61 @@
export {
AlreadyConnectedError,
NotConnectedError,
LiveAcpConnection,
SandboxAgent,
SandboxAgentClient,
SandboxAgentError,
Session,
} from "./client.ts";
export { AcpRpcError } from "acp-http-client";
export { buildInspectorUrl } from "./inspector.ts";
export type {
AgentEvent,
AgentUnparsedNotification,
ListModelsResponse,
PermissionRequest,
PermissionResponse,
SandboxAgentClientConnectOptions,
SandboxAgentClientOptions,
SandboxAgentConnectOptions,
SandboxAgentEventObserver,
SandboxAgentStartOptions,
SandboxMetadata,
SessionCreateRequest,
SessionModelInfo,
SessionUpdateNotification,
SessionResumeOrCreateRequest,
SessionSendOptions,
SessionEventListener,
} from "./client.ts";
export type {
InspectorUrlOptions,
} from "./inspector.ts";
export type { InspectorUrlOptions } from "./inspector.ts";
export {
InMemorySessionPersistDriver,
} from "./types.ts";
export type {
AgentCapabilities,
AcpEnvelope,
AcpServerInfo,
AcpServerListResponse,
AgentInfo,
AgentInstallArtifact,
AgentInstallRequest,
AgentInstallResponse,
AgentListResponse,
FsActionResponse,
FsDeleteQuery,
FsEntriesQuery,
FsEntry,
FsMoveRequest,
FsMoveResponse,
FsPathQuery,
FsStat,
FsUploadBatchQuery,
FsUploadBatchResponse,
FsWriteResponse,
HealthResponse,
InMemorySessionPersistDriverOptions,
ListEventsRequest,
ListPage,
ListPageRequest,
McpConfigQuery,
McpServerConfig,
ProblemDetails,
SessionInfo,
SessionListResponse,
SessionTerminateResponse,
SessionEvent,
SessionPersistDriver,
SessionRecord,
SkillsConfig,
SkillsConfigQuery,
} from "./types.ts";
export type {

View file

@ -207,7 +207,7 @@ async function waitForHealth(
throw new Error("sandbox-agent exited before becoming healthy.");
}
try {
const response = await fetcher(`${baseUrl}/v2/health`, {
const response = await fetcher(`${baseUrl}/v1/health`, {
headers: { Authorization: `Bearer ${token}` },
});
if (response.ok) {

View file

@ -1,282 +1,237 @@
export interface ProblemDetails {
type: string;
title: string;
status: number;
detail?: string;
instance?: string;
[key: string]: unknown;
}
import type { AnyMessage, NewSessionRequest } from "acp-http-client";
import type { components, operations } from "./generated/openapi.ts";
export type HealthStatus = "healthy" | "degraded" | "unhealthy" | "ok";
export type ProblemDetails = components["schemas"]["ProblemDetails"];
export interface AgentHealthInfo {
export type HealthResponse = JsonResponse<operations["get_v1_health"], 200>;
export type AgentListResponse = JsonResponse<operations["get_v1_agents"], 200>;
export type AgentInfo = components["schemas"]["AgentInfo"];
export type AgentInstallRequest = JsonRequestBody<operations["post_v1_agent_install"]>;
export type AgentInstallResponse = JsonResponse<operations["post_v1_agent_install"], 200>;
export type AcpEnvelope = components["schemas"]["AcpEnvelope"];
export type AcpServerInfo = components["schemas"]["AcpServerInfo"];
export type AcpServerListResponse = JsonResponse<operations["get_v1_acp_servers"], 200>;
export type FsEntriesQuery = QueryParams<operations["get_v1_fs_entries"]>;
export type FsEntry = components["schemas"]["FsEntry"];
export type FsPathQuery = QueryParams<operations["get_v1_fs_file"]>;
export type FsDeleteQuery = QueryParams<operations["delete_v1_fs_entry"]>;
export type FsUploadBatchQuery = QueryParams<operations["post_v1_fs_upload_batch"]>;
export type FsWriteResponse = JsonResponse<operations["put_v1_fs_file"], 200>;
export type FsActionResponse = JsonResponse<operations["delete_v1_fs_entry"], 200>;
export type FsMoveRequest = JsonRequestBody<operations["post_v1_fs_move"]>;
export type FsMoveResponse = JsonResponse<operations["post_v1_fs_move"], 200>;
export type FsStat = JsonResponse<operations["get_v1_fs_stat"], 200>;
export type FsUploadBatchResponse = JsonResponse<operations["post_v1_fs_upload_batch"], 200>;
export type McpConfigQuery = QueryParams<operations["get_v1_config_mcp"]>;
export type McpServerConfig = components["schemas"]["McpServerConfig"];
export type SkillsConfigQuery = QueryParams<operations["get_v1_config_skills"]>;
export type SkillsConfig = components["schemas"]["SkillsConfig"];
export interface SessionRecord {
id: string;
agent: string;
installed: boolean;
running: boolean;
[key: string]: unknown;
agentSessionId: string;
lastConnectionId: string;
createdAt: number;
destroyedAt?: number;
sessionInit?: Omit<NewSessionRequest, "_meta">;
}
export interface HealthResponse {
status: HealthStatus | string;
version: string;
uptime_ms: number;
agents: AgentHealthInfo[];
// Backward-compatible field from earlier v2 payloads.
api_version?: string;
[key: string]: unknown;
}
export type SessionEventSender = "client" | "agent";
export type ServerStatus = "running" | "stopped" | "error";
export interface ServerStatusInfo {
status: ServerStatus | string;
base_url?: string | null;
baseUrl?: string | null;
uptime_ms?: number | null;
uptimeMs?: number | null;
restart_count?: number;
restartCount?: number;
last_error?: string | null;
lastError?: string | null;
[key: string]: unknown;
}
export interface AgentModelInfo {
id?: string;
model_id?: string;
modelId?: string;
name?: string | null;
description?: string | null;
default_variant?: string | null;
defaultVariant?: string | null;
variants?: string[] | null;
[key: string]: unknown;
}
export interface AgentModeInfo {
export interface SessionEvent {
// Stable unique event id. For ordering, sort by (sessionId, eventIndex).
id: string;
name: string;
description: string;
[key: string]: unknown;
eventIndex: number;
sessionId: string;
createdAt: number;
connectionId: string;
sender: SessionEventSender;
payload: AnyMessage;
}
export interface AgentCapabilities {
plan_mode?: boolean;
permissions?: boolean;
questions?: boolean;
tool_calls?: boolean;
tool_results?: boolean;
text_messages?: boolean;
images?: boolean;
file_attachments?: boolean;
session_lifecycle?: boolean;
error_events?: boolean;
reasoning?: boolean;
status?: boolean;
command_execution?: boolean;
file_changes?: boolean;
mcp_tools?: boolean;
streaming_deltas?: boolean;
item_started?: boolean;
shared_process?: boolean;
unstable_methods?: boolean;
[key: string]: unknown;
export interface ListPageRequest {
cursor?: string;
limit?: number;
}
export interface AgentInfo {
id: string;
installed?: boolean;
credentials_available?: boolean;
native_required?: boolean;
native_installed?: boolean;
native_version?: string | null;
agent_process_installed?: boolean;
agent_process_source?: string | null;
agent_process_version?: string | null;
version?: string | null;
path?: string | null;
server_status?: ServerStatusInfo | null;
models?: AgentModelInfo[] | null;
default_model?: string | null;
modes?: AgentModeInfo[] | null;
capabilities: AgentCapabilities;
[key: string]: unknown;
export interface ListPage<T> {
items: T[];
nextCursor?: string;
}
export interface AgentListResponse {
agents: AgentInfo[];
export interface ListEventsRequest extends ListPageRequest {
sessionId: string;
}
export interface AgentInstallRequest {
reinstall?: boolean;
agentVersion?: string;
agentProcessVersion?: string;
export interface SessionPersistDriver {
getSession(id: string): Promise<SessionRecord | null>;
listSessions(request?: ListPageRequest): Promise<ListPage<SessionRecord>>;
updateSession(session: SessionRecord): Promise<void>;
listEvents(request: ListEventsRequest): Promise<ListPage<SessionEvent>>;
insertEvent(event: SessionEvent): Promise<void>;
}
export interface AgentInstallArtifact {
kind: string;
path: string;
source: string;
version?: string | null;
export interface InMemorySessionPersistDriverOptions {
maxSessions?: number;
maxEventsPerSession?: number;
}
export interface AgentInstallResponse {
already_installed: boolean;
artifacts: AgentInstallArtifact[];
const DEFAULT_MAX_SESSIONS = 1024;
const DEFAULT_MAX_EVENTS_PER_SESSION = 500;
const DEFAULT_LIST_LIMIT = 100;
export class InMemorySessionPersistDriver implements SessionPersistDriver {
private readonly maxSessions: number;
private readonly maxEventsPerSession: number;
private readonly sessions = new Map<string, SessionRecord>();
private readonly eventsBySession = new Map<string, SessionEvent[]>();
constructor(options: InMemorySessionPersistDriverOptions = {}) {
this.maxSessions = normalizeCap(options.maxSessions, DEFAULT_MAX_SESSIONS);
this.maxEventsPerSession = normalizeCap(
options.maxEventsPerSession,
DEFAULT_MAX_EVENTS_PER_SESSION,
);
}
async getSession(id: string): Promise<SessionRecord | null> {
const session = this.sessions.get(id);
return session ? cloneSessionRecord(session) : null;
}
async listSessions(request: ListPageRequest = {}): Promise<ListPage<SessionRecord>> {
const sorted = [...this.sessions.values()].sort((a, b) => {
if (a.createdAt !== b.createdAt) {
return a.createdAt - b.createdAt;
}
return a.id.localeCompare(b.id);
});
const page = paginate(sorted, request);
return {
items: page.items.map(cloneSessionRecord),
nextCursor: page.nextCursor,
};
}
async updateSession(session: SessionRecord): Promise<void> {
this.sessions.set(session.id, { ...session });
if (!this.eventsBySession.has(session.id)) {
this.eventsBySession.set(session.id, []);
}
if (this.sessions.size <= this.maxSessions) {
return;
}
const overflow = this.sessions.size - this.maxSessions;
const removable = [...this.sessions.values()]
.sort((a, b) => {
if (a.createdAt !== b.createdAt) {
return a.createdAt - b.createdAt;
}
return a.id.localeCompare(b.id);
})
.slice(0, overflow)
.map((sessionToRemove) => sessionToRemove.id);
for (const sessionId of removable) {
this.sessions.delete(sessionId);
this.eventsBySession.delete(sessionId);
}
}
async listEvents(request: ListEventsRequest): Promise<ListPage<SessionEvent>> {
const all = [...(this.eventsBySession.get(request.sessionId) ?? [])].sort((a, b) => {
if (a.eventIndex !== b.eventIndex) {
return a.eventIndex - b.eventIndex;
}
return a.id.localeCompare(b.id);
});
const page = paginate(all, request);
return {
items: page.items.map(cloneSessionEvent),
nextCursor: page.nextCursor,
};
}
async insertEvent(event: SessionEvent): Promise<void> {
const events = this.eventsBySession.get(event.sessionId) ?? [];
events.push(cloneSessionEvent(event));
if (events.length > this.maxEventsPerSession) {
events.splice(0, events.length - this.maxEventsPerSession);
}
this.eventsBySession.set(event.sessionId, events);
}
}
export type SessionEndReason = "completed" | "error" | "terminated";
export type TerminatedBy = "agent" | "daemon";
export interface StderrOutput {
head?: string | null;
tail?: string | null;
truncated: boolean;
total_lines?: number | null;
function cloneSessionRecord(session: SessionRecord): SessionRecord {
return {
...session,
sessionInit: session.sessionInit
? (JSON.parse(JSON.stringify(session.sessionInit)) as SessionRecord["sessionInit"])
: undefined,
};
}
export interface SessionTerminationInfo {
reason: SessionEndReason | string;
terminated_by: TerminatedBy | string;
message?: string | null;
exit_code?: number | null;
stderr?: StderrOutput | null;
[key: string]: unknown;
function cloneSessionEvent(event: SessionEvent): SessionEvent {
return {
...event,
payload: JSON.parse(JSON.stringify(event.payload)) as AnyMessage,
};
}
export interface SessionInfo {
session_id: string;
sessionId?: string;
agent?: string;
cwd?: string;
title?: string | null;
ended?: boolean;
created_at?: string | number | null;
createdAt?: string | number | null;
updated_at?: string | number | null;
updatedAt?: string | number | null;
model?: string | null;
metadata?: Record<string, unknown> | null;
agent_mode?: string;
agentMode?: string;
permission_mode?: string;
permissionMode?: string;
native_session_id?: string | null;
nativeSessionId?: string | null;
event_count?: number;
eventCount?: number;
directory?: string | null;
variant?: string | null;
mcp?: Record<string, unknown> | null;
skills?: Record<string, unknown> | null;
termination_info?: SessionTerminationInfo | null;
terminationInfo?: SessionTerminationInfo | null;
[key: string]: unknown;
type ResponsesOf<T> = T extends { responses: infer R } ? R : never;
type JsonResponse<T, StatusCode extends keyof ResponsesOf<T>> = ResponsesOf<T>[StatusCode] extends {
content: { "application/json": infer B };
}
? B
: never;
type JsonRequestBody<T> = T extends {
requestBody: { content: { "application/json": infer B } };
}
? B
: never;
type QueryParams<T> = T extends { parameters: { query: infer Q } }
? Q
: T extends { parameters: { query?: infer Q } }
? Q
: never;
function normalizeCap(value: number | undefined, fallback: number): number {
if (!Number.isFinite(value) || (value ?? 0) < 1) {
return fallback;
}
return Math.floor(value as number);
}
export interface SessionListResponse {
sessions: SessionInfo[];
function paginate<T>(items: T[], request: ListPageRequest): ListPage<T> {
const offset = parseCursor(request.cursor);
const limit = normalizeCap(request.limit, DEFAULT_LIST_LIMIT);
const slice = items.slice(offset, offset + limit);
const nextOffset = offset + slice.length;
return {
items: slice,
nextCursor: nextOffset < items.length ? String(nextOffset) : undefined,
};
}
export interface SessionTerminateResponse {
terminated?: boolean;
reason?: SessionEndReason | string;
terminated_by?: TerminatedBy | string;
terminatedBy?: TerminatedBy | string;
[key: string]: unknown;
}
export interface SessionEndedParams {
session_id?: string;
sessionId?: string;
data?: SessionTerminationInfo;
reason?: SessionEndReason | string;
terminated_by?: TerminatedBy | string;
terminatedBy?: TerminatedBy | string;
message?: string | null;
exit_code?: number | null;
stderr?: StderrOutput | null;
[key: string]: unknown;
}
export interface SessionEndedNotification {
jsonrpc: "2.0";
method: "_sandboxagent/session/ended";
params: SessionEndedParams;
[key: string]: unknown;
}
export interface FsPathQuery {
path: string;
session_id?: string | null;
sessionId?: string | null;
}
export interface FsEntriesQuery {
path?: string | null;
session_id?: string | null;
sessionId?: string | null;
}
export interface FsSessionQuery {
session_id?: string | null;
sessionId?: string | null;
}
export interface FsDeleteQuery {
path: string;
recursive?: boolean | null;
session_id?: string | null;
sessionId?: string | null;
}
export interface FsUploadBatchQuery {
path?: string | null;
session_id?: string | null;
sessionId?: string | null;
}
export type FsEntryType = "file" | "directory";
export interface FsEntry {
name: string;
path: string;
size: number;
entry_type?: FsEntryType;
entryType?: FsEntryType;
modified?: string | null;
}
export interface FsStat {
path: string;
size: number;
entry_type?: FsEntryType;
entryType?: FsEntryType;
modified?: string | null;
}
export interface FsWriteResponse {
path: string;
bytes_written?: number;
bytesWritten?: number;
}
export interface FsMoveRequest {
from: string;
to: string;
overwrite?: boolean | null;
}
export interface FsMoveResponse {
from: string;
to: string;
}
export interface FsActionResponse {
path: string;
}
export interface FsUploadBatchResponse {
paths: string[];
truncated: boolean;
function parseCursor(cursor: string | undefined): number {
if (!cursor) {
return 0;
}
const parsed = Number.parseInt(cursor, 10);
if (!Number.isFinite(parsed) || parsed < 0) {
return 0;
}
return parsed;
}