@aomi-labs/client 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +299 -0
- package/dist/cli.js +2416 -0
- package/dist/index.cjs +494 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +258 -27
- package/dist/index.d.ts +258 -27
- package/dist/index.js +488 -3
- package/dist/index.js.map +1 -1
- package/package.json +10 -2
- package/skills/aomi-transact/SKILL.md +164 -0
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/sse.ts","../src/client.ts","../src/types.ts"],"sourcesContent":["import type { ApiSSEEvent, Logger } from \"./types\";\n\nexport type SseSubscriber = {\n subscribe: (\n sessionId: string,\n onUpdate: (event: ApiSSEEvent) => void,\n onError?: (error: unknown) => void,\n ) => () => void;\n};\n\nexport type SseSubscriberOptions = {\n backendUrl: string;\n getHeaders: (sessionId: string) => HeadersInit;\n logger?: Logger;\n};\n\ntype SseSubscription = {\n abortController: AbortController | null;\n retries: number;\n retryTimer: ReturnType<typeof setTimeout> | null;\n stopped: boolean;\n listeners: Set<SseListener>;\n stop: (reason?: string) => void;\n};\n\ntype SseListener = {\n onUpdate: (event: ApiSSEEvent) => void;\n onError?: (error: unknown) => void;\n};\n\nfunction extractSseData(rawEvent: string): string | null {\n const dataLines = rawEvent\n .split(\"\\n\")\n .filter((line) => line.startsWith(\"data:\"))\n .map((line) => line.slice(5).trimStart());\n if (!dataLines.length) return null;\n return dataLines.join(\"\\n\");\n}\n\nasync function readSseStream(\n stream: ReadableStream<Uint8Array>,\n signal: AbortSignal,\n onMessage: (data: string) => void,\n): Promise<void> {\n const reader = stream.getReader();\n const decoder = new TextDecoder();\n let buffer = \"\";\n\n try {\n while (!signal.aborted) {\n const { value, done } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n buffer = buffer.replace(/\\r/g, \"\");\n\n let separatorIndex = buffer.indexOf(\"\\n\\n\");\n while (separatorIndex >= 0) {\n const rawEvent = buffer.slice(0, separatorIndex);\n buffer = buffer.slice(separatorIndex + 2);\n const data = extractSseData(rawEvent);\n if (data) {\n onMessage(data);\n }\n separatorIndex = buffer.indexOf(\"\\n\\n\");\n }\n }\n } finally {\n reader.releaseLock();\n }\n}\n\nexport function createSseSubscriber({\n backendUrl,\n getHeaders,\n logger,\n}: SseSubscriberOptions): SseSubscriber {\n const subscriptions = new Map<string, SseSubscription>();\n\n const subscribe: SseSubscriber[\"subscribe\"] = (\n sessionId,\n onUpdate,\n onError,\n ) => {\n const existing = subscriptions.get(sessionId);\n const listener: SseListener = { onUpdate, onError };\n if (existing) {\n existing.listeners.add(listener);\n logger?.debug(\"[aomi][sse] listener added\", {\n sessionId,\n listeners: existing.listeners.size,\n });\n return () => {\n existing.listeners.delete(listener);\n logger?.debug(\"[aomi][sse] listener removed\", {\n sessionId,\n listeners: existing.listeners.size,\n });\n if (existing.listeners.size === 0) {\n existing.stop(\"unsubscribe\");\n if (subscriptions.get(sessionId) === existing) {\n subscriptions.delete(sessionId);\n }\n }\n };\n }\n\n const subscription: SseSubscription = {\n abortController: null,\n retries: 0,\n retryTimer: null,\n stopped: false,\n listeners: new Set([listener]),\n stop: (reason?: string) => {\n subscription.stopped = true;\n if (subscription.retryTimer) {\n clearTimeout(subscription.retryTimer);\n subscription.retryTimer = null;\n }\n subscription.abortController?.abort();\n subscription.abortController = null;\n logger?.debug(\"[aomi][sse] stop\", {\n sessionId,\n reason,\n retries: subscription.retries,\n });\n },\n };\n\n const scheduleRetry = () => {\n if (subscription.stopped) return;\n subscription.retries += 1;\n const delayMs = Math.min(500 * 2 ** (subscription.retries - 1), 10000);\n logger?.debug(\"[aomi][sse] retry scheduled\", {\n sessionId,\n delayMs,\n retries: subscription.retries,\n });\n subscription.retryTimer = setTimeout(() => {\n void open();\n }, delayMs);\n };\n\n const open = async () => {\n if (subscription.stopped) return;\n if (subscription.retryTimer) {\n clearTimeout(subscription.retryTimer);\n subscription.retryTimer = null;\n }\n\n const controller = new AbortController();\n subscription.abortController = controller;\n const openedAt = Date.now();\n\n try {\n const response = await fetch(`${backendUrl}/api/updates`, {\n headers: getHeaders(sessionId),\n signal: controller.signal,\n });\n\n if (!response.ok) {\n throw new Error(\n `SSE HTTP ${response.status}: ${response.statusText}`,\n );\n }\n\n if (!response.body) {\n throw new Error(\"SSE response missing body\");\n }\n\n subscription.retries = 0;\n\n await readSseStream(response.body, controller.signal, (data) => {\n let parsed: ApiSSEEvent;\n try {\n parsed = JSON.parse(data) as ApiSSEEvent;\n } catch (error) {\n for (const item of subscription.listeners) {\n item.onError?.(error);\n }\n return;\n }\n\n for (const item of subscription.listeners) {\n try {\n item.onUpdate(parsed);\n } catch (error) {\n item.onError?.(error);\n }\n }\n });\n logger?.debug(\"[aomi][sse] stream ended\", {\n sessionId,\n aborted: controller.signal.aborted,\n stopped: subscription.stopped,\n durationMs: Date.now() - openedAt,\n });\n } catch (error) {\n if (!controller.signal.aborted && !subscription.stopped) {\n for (const item of subscription.listeners) {\n item.onError?.(error);\n }\n }\n }\n\n if (!subscription.stopped) {\n scheduleRetry();\n }\n };\n\n subscriptions.set(sessionId, subscription);\n void open();\n\n return () => {\n subscription.listeners.delete(listener);\n logger?.debug(\"[aomi][sse] listener removed\", {\n sessionId,\n listeners: subscription.listeners.size,\n });\n if (subscription.listeners.size === 0) {\n subscription.stop(\"unsubscribe\");\n if (subscriptions.get(sessionId) === subscription) {\n subscriptions.delete(sessionId);\n }\n }\n };\n };\n\n return { subscribe };\n}\n","import type {\n AomiClientOptions,\n AomiMessage,\n ApiChatResponse,\n ApiCreateThreadResponse,\n ApiInterruptResponse,\n ApiSSEEvent,\n ApiStateResponse,\n ApiSystemEvent,\n ApiSystemResponse,\n ApiThread,\n Logger,\n UserState,\n} from \"./types\";\nimport { createSseSubscriber, type SseSubscriber } from \"./sse\";\n\n// =============================================================================\n// Internal helpers\n// =============================================================================\n\nconst SESSION_ID_HEADER = \"X-Session-Id\";\nconst API_KEY_HEADER = \"X-API-Key\";\n\nfunction toQueryString(payload: Record<string, unknown>): string {\n const params = new URLSearchParams();\n for (const [key, value] of Object.entries(payload)) {\n if (value === undefined || value === null) continue;\n params.set(key, String(value));\n }\n const qs = params.toString();\n return qs ? `?${qs}` : \"\";\n}\n\nfunction withSessionHeader(\n sessionId: string,\n init?: HeadersInit,\n): HeadersInit {\n const headers = new Headers(init);\n headers.set(SESSION_ID_HEADER, sessionId);\n return headers;\n}\n\nasync function postState<T>(\n baseUrl: string,\n path: string,\n payload: Record<string, unknown>,\n sessionId: string,\n apiKey?: string,\n): Promise<T> {\n const query = toQueryString(payload);\n const url = `${baseUrl}${path}${query}`;\n\n const headers = new Headers(withSessionHeader(sessionId));\n if (apiKey) {\n headers.set(API_KEY_HEADER, apiKey);\n }\n\n const response = await fetch(url, {\n method: \"POST\",\n headers,\n });\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n }\n\n return (await response.json()) as T;\n}\n\n// =============================================================================\n// AomiClient\n// =============================================================================\n\nexport class AomiClient {\n private readonly baseUrl: string;\n private readonly apiKey?: string;\n private readonly logger?: Logger;\n private readonly sseSubscriber: SseSubscriber;\n\n constructor(options: AomiClientOptions) {\n // Strip trailing slash\n this.baseUrl = options.baseUrl.replace(/\\/+$/, \"\");\n this.apiKey = options.apiKey;\n this.logger = options.logger;\n\n this.sseSubscriber = createSseSubscriber({\n backendUrl: this.baseUrl,\n getHeaders: (sessionId) =>\n withSessionHeader(sessionId, { Accept: \"text/event-stream\" }),\n logger: this.logger,\n });\n }\n\n // ===========================================================================\n // Chat & State\n // ===========================================================================\n\n /**\n * Fetch current session state (messages, processing status, title).\n */\n async fetchState(\n sessionId: string,\n userState?: UserState,\n ): Promise<ApiStateResponse> {\n const url = new URL(\"/api/state\", this.baseUrl);\n if (userState) {\n url.searchParams.set(\"user_state\", JSON.stringify(userState));\n }\n\n const response = await fetch(url.toString(), {\n headers: withSessionHeader(sessionId),\n });\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n }\n\n return (await response.json()) as ApiStateResponse;\n }\n\n /**\n * Send a chat message and return updated session state.\n */\n async sendMessage(\n sessionId: string,\n message: string,\n options?: {\n namespace?: string;\n publicKey?: string;\n apiKey?: string;\n userState?: UserState;\n },\n ): Promise<ApiChatResponse> {\n const namespace = options?.namespace ?? \"default\";\n const apiKey = options?.apiKey ?? this.apiKey;\n\n const payload: Record<string, unknown> = { message, namespace };\n if (options?.publicKey) {\n payload.public_key = options.publicKey;\n }\n if (options?.userState) {\n payload.user_state = JSON.stringify(options.userState);\n }\n\n return postState<ApiChatResponse>(\n this.baseUrl,\n \"/api/chat\",\n payload,\n sessionId,\n apiKey,\n );\n }\n\n /**\n * Send a system-level message (e.g. wallet state changes, context switches).\n */\n async sendSystemMessage(\n sessionId: string,\n message: string,\n ): Promise<ApiSystemResponse> {\n return postState<ApiSystemResponse>(\n this.baseUrl,\n \"/api/system\",\n { message },\n sessionId,\n );\n }\n\n /**\n * Interrupt the AI's current response.\n */\n async interrupt(sessionId: string): Promise<ApiInterruptResponse> {\n return postState<ApiInterruptResponse>(\n this.baseUrl,\n \"/api/interrupt\",\n {},\n sessionId,\n );\n }\n\n // ===========================================================================\n // SSE (Real-time Updates)\n // ===========================================================================\n\n /**\n * Subscribe to real-time SSE updates for a session.\n * Automatically reconnects with exponential backoff on disconnects.\n * Returns an unsubscribe function.\n */\n subscribeSSE(\n sessionId: string,\n onUpdate: (event: ApiSSEEvent) => void,\n onError?: (error: unknown) => void,\n ): () => void {\n return this.sseSubscriber.subscribe(sessionId, onUpdate, onError);\n }\n\n // ===========================================================================\n // Thread / Session Management\n // ===========================================================================\n\n /**\n * List all threads for a wallet address.\n */\n async listThreads(publicKey: string): Promise<ApiThread[]> {\n const url = `${this.baseUrl}/api/sessions?public_key=${encodeURIComponent(publicKey)}`;\n const response = await fetch(url);\n\n if (!response.ok) {\n throw new Error(`Failed to fetch threads: HTTP ${response.status}`);\n }\n\n return (await response.json()) as ApiThread[];\n }\n\n /**\n * Get a single thread by ID.\n */\n async getThread(sessionId: string): Promise<ApiThread> {\n const url = `${this.baseUrl}/api/sessions/${encodeURIComponent(sessionId)}`;\n const response = await fetch(url, {\n headers: withSessionHeader(sessionId),\n });\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n }\n\n return (await response.json()) as ApiThread;\n }\n\n /**\n * Create a new thread. The client generates the session ID.\n */\n async createThread(\n threadId: string,\n publicKey?: string,\n ): Promise<ApiCreateThreadResponse> {\n const body: Record<string, string> = {};\n if (publicKey) body.public_key = publicKey;\n\n const url = `${this.baseUrl}/api/sessions`;\n const response = await fetch(url, {\n method: \"POST\",\n headers: withSessionHeader(threadId, {\n \"Content-Type\": \"application/json\",\n }),\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n throw new Error(`Failed to create thread: HTTP ${response.status}`);\n }\n\n return (await response.json()) as ApiCreateThreadResponse;\n }\n\n /**\n * Delete a thread by ID.\n */\n async deleteThread(sessionId: string): Promise<void> {\n const url = `${this.baseUrl}/api/sessions/${encodeURIComponent(sessionId)}`;\n const response = await fetch(url, {\n method: \"DELETE\",\n headers: withSessionHeader(sessionId),\n });\n\n if (!response.ok) {\n throw new Error(`Failed to delete thread: HTTP ${response.status}`);\n }\n }\n\n /**\n * Rename a thread.\n */\n async renameThread(sessionId: string, newTitle: string): Promise<void> {\n const url = `${this.baseUrl}/api/sessions/${encodeURIComponent(sessionId)}`;\n const response = await fetch(url, {\n method: \"PATCH\",\n headers: withSessionHeader(sessionId, {\n \"Content-Type\": \"application/json\",\n }),\n body: JSON.stringify({ title: newTitle }),\n });\n\n if (!response.ok) {\n throw new Error(`Failed to rename thread: HTTP ${response.status}`);\n }\n }\n\n /**\n * Archive a thread.\n */\n async archiveThread(sessionId: string): Promise<void> {\n const url = `${this.baseUrl}/api/sessions/${encodeURIComponent(sessionId)}/archive`;\n const response = await fetch(url, {\n method: \"POST\",\n headers: withSessionHeader(sessionId),\n });\n\n if (!response.ok) {\n throw new Error(`Failed to archive thread: HTTP ${response.status}`);\n }\n }\n\n /**\n * Unarchive a thread.\n */\n async unarchiveThread(sessionId: string): Promise<void> {\n const url = `${this.baseUrl}/api/sessions/${encodeURIComponent(sessionId)}/unarchive`;\n const response = await fetch(url, {\n method: \"POST\",\n headers: withSessionHeader(sessionId),\n });\n\n if (!response.ok) {\n throw new Error(`Failed to unarchive thread: HTTP ${response.status}`);\n }\n }\n\n // ===========================================================================\n // System Events\n // ===========================================================================\n\n /**\n * Get system events for a session.\n */\n async getSystemEvents(\n sessionId: string,\n count?: number,\n ): Promise<ApiSystemEvent[]> {\n const url = new URL(\"/api/events\", this.baseUrl);\n if (count !== undefined) {\n url.searchParams.set(\"count\", String(count));\n }\n const response = await fetch(url.toString(), {\n headers: withSessionHeader(sessionId),\n });\n\n if (!response.ok) {\n if (response.status === 404) return [];\n throw new Error(`Failed to get system events: HTTP ${response.status}`);\n }\n\n return (await response.json()) as ApiSystemEvent[];\n }\n\n // ===========================================================================\n // Control API\n // ===========================================================================\n\n /**\n * Get available namespaces.\n */\n async getNamespaces(\n sessionId: string,\n options?: { publicKey?: string; apiKey?: string },\n ): Promise<string[]> {\n const url = new URL(\"/api/control/namespaces\", this.baseUrl);\n if (options?.publicKey) {\n url.searchParams.set(\"public_key\", options.publicKey);\n }\n\n const apiKey = options?.apiKey ?? this.apiKey;\n const headers = new Headers(withSessionHeader(sessionId));\n if (apiKey) {\n headers.set(API_KEY_HEADER, apiKey);\n }\n\n const response = await fetch(url.toString(), { headers });\n\n if (!response.ok) {\n throw new Error(`Failed to get namespaces: HTTP ${response.status}`);\n }\n\n return (await response.json()) as string[];\n }\n\n /**\n * Get available models.\n */\n async getModels(sessionId: string): Promise<string[]> {\n const url = new URL(\"/api/control/models\", this.baseUrl);\n\n const response = await fetch(url.toString(), {\n headers: withSessionHeader(sessionId),\n });\n\n if (!response.ok) {\n throw new Error(`Failed to get models: HTTP ${response.status}`);\n }\n\n return (await response.json()) as string[];\n }\n\n /**\n * Set the model for a session.\n */\n async setModel(\n sessionId: string,\n rig: string,\n options?: { namespace?: string; apiKey?: string },\n ): Promise<{\n success: boolean;\n rig: string;\n baml: string;\n created: boolean;\n }> {\n const apiKey = options?.apiKey ?? this.apiKey;\n const payload: Record<string, unknown> = { rig };\n if (options?.namespace) {\n payload.namespace = options.namespace;\n }\n\n return postState<{\n success: boolean;\n rig: string;\n baml: string;\n created: boolean;\n }>(this.baseUrl, \"/api/control/model\", payload, sessionId, apiKey);\n }\n}\n","// =============================================================================\n// User State\n// =============================================================================\n\n/**\n * Client-side user state synced with the backend.\n * Typically wallet connection info, but can be any key-value data.\n */\nexport type UserState = Record<string, unknown>;\n\n// =============================================================================\n// Logger\n// =============================================================================\n\n/**\n * Optional logger for debug output. Pass `console` or any compatible object.\n */\nexport type Logger = {\n debug: (...args: unknown[]) => void;\n};\n\n// =============================================================================\n// Client Options\n// =============================================================================\n\nexport type AomiClientOptions = {\n /** Base URL of the Aomi backend (e.g. \"https://aomi.dev\") */\n baseUrl: string;\n /** Default API key for non-default namespaces */\n apiKey?: string;\n /** Optional logger for debug output (default: silent) */\n logger?: Logger;\n};\n\n// =============================================================================\n// Base Types\n// =============================================================================\n\nexport interface AomiMessage {\n sender?: \"user\" | \"agent\" | \"system\" | string;\n content?: string;\n timestamp?: string;\n is_streaming?: boolean;\n tool_result?: [string, string] | null;\n}\n\n// =============================================================================\n// API Response Types\n// =============================================================================\n\n/**\n * GET /api/state\n * Fetches current session state including messages and processing status\n */\nexport interface ApiStateResponse {\n messages?: AomiMessage[] | null;\n system_events?: ApiSystemEvent[] | null;\n title?: string | null;\n is_processing?: boolean;\n}\n\n/**\n * POST /api/chat\n * Sends a chat message and returns updated session state\n */\nexport interface ApiChatResponse {\n messages?: AomiMessage[] | null;\n system_events?: ApiSystemEvent[] | null;\n title?: string | null;\n is_processing?: boolean;\n}\n\n/**\n * POST /api/system\n * Sends a system message and returns the response message\n */\nexport interface ApiSystemResponse {\n res?: AomiMessage | null;\n}\n\n/**\n * POST /api/interrupt\n * Interrupts current processing and returns updated session state\n */\nexport type ApiInterruptResponse = ApiChatResponse;\n\n/**\n * GET /api/sessions\n * Returns array of ApiThread\n */\nexport interface ApiThread {\n session_id: string;\n title: string;\n is_archived?: boolean;\n}\n\n/**\n * POST /api/sessions\n * Creates a new thread/session\n */\nexport interface ApiCreateThreadResponse {\n session_id: string;\n title?: string;\n}\n\n// =============================================================================\n// SSE Event Types (/api/updates)\n// =============================================================================\n\n/**\n * Base SSE event - all events have session_id and type\n */\nexport type ApiSSEEvent = {\n type:\n | \"title_changed\"\n | \"tool_update\"\n | \"tool_complete\"\n | \"system_notice\"\n | string;\n session_id: string;\n new_title?: string;\n [key: string]: unknown;\n};\n\nexport type ApiSSEEventType =\n | \"title_changed\"\n | \"tool_update\"\n | \"tool_complete\"\n | \"system_notice\";\n\n// =============================================================================\n// System Events (/api/events)\n// =============================================================================\n\n/**\n * Backend SystemEvent enum serializes as tagged JSON:\n * - InlineCall: {\"InlineCall\": {\"type\": \"wallet_tx_request\", \"payload\": {...}}}\n * - SystemNotice: {\"SystemNotice\": \"message\"}\n * - SystemError: {\"SystemError\": \"message\"}\n * - AsyncCallback: {\"AsyncCallback\": {...}} (not sent over HTTP)\n */\nexport type ApiSystemEvent =\n | { InlineCall: { type: string; payload?: unknown; [key: string]: unknown } }\n | { SystemNotice: string }\n | { SystemError: string }\n | { AsyncCallback: Record<string, unknown> };\n\n// =============================================================================\n// Type Guards\n// =============================================================================\n\nexport function isInlineCall(\n event: ApiSystemEvent,\n): event is { InlineCall: { type: string; payload?: unknown } } {\n return \"InlineCall\" in event;\n}\n\nexport function isSystemNotice(\n event: ApiSystemEvent,\n): event is { SystemNotice: string } {\n return \"SystemNotice\" in event;\n}\n\nexport function isSystemError(\n event: ApiSystemEvent,\n): event is { SystemError: string } {\n return \"SystemError\" in event;\n}\n\nexport function isAsyncCallback(\n event: ApiSystemEvent,\n): event is { AsyncCallback: Record<string, unknown> } {\n return \"AsyncCallback\" in event;\n}\n"],"mappings":"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","names":["_a"]}
|
|
1
|
+
{"version":3,"sources":["../src/sse.ts","../src/client.ts","../src/types.ts","../src/event-emitter.ts","../src/event-unwrap.ts","../src/wallet-utils.ts","../src/session.ts"],"sourcesContent":["import type { AomiSSEEvent, Logger } from \"./types\";\n\nexport type SseSubscriber = {\n subscribe: (\n sessionId: string,\n onUpdate: (event: AomiSSEEvent) => void,\n onError?: (error: unknown) => void,\n ) => () => void;\n};\n\nexport type SseSubscriberOptions = {\n backendUrl: string;\n getHeaders: (sessionId: string) => HeadersInit;\n logger?: Logger;\n};\n\ntype SseSubscription = {\n abortController: AbortController | null;\n retries: number;\n retryTimer: ReturnType<typeof setTimeout> | null;\n stopped: boolean;\n listeners: Set<SseListener>;\n stop: (reason?: string) => void;\n};\n\ntype SseListener = {\n onUpdate: (event: AomiSSEEvent) => void;\n onError?: (error: unknown) => void;\n};\n\nfunction extractSseData(rawEvent: string): string | null {\n const dataLines = rawEvent\n .split(\"\\n\")\n .filter((line) => line.startsWith(\"data:\"))\n .map((line) => line.slice(5).trimStart());\n if (!dataLines.length) return null;\n return dataLines.join(\"\\n\");\n}\n\nasync function readSseStream(\n stream: ReadableStream<Uint8Array>,\n signal: AbortSignal,\n onMessage: (data: string) => void,\n): Promise<void> {\n const reader = stream.getReader();\n const decoder = new TextDecoder();\n let buffer = \"\";\n\n try {\n while (!signal.aborted) {\n const { value, done } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n buffer = buffer.replace(/\\r/g, \"\");\n\n let separatorIndex = buffer.indexOf(\"\\n\\n\");\n while (separatorIndex >= 0) {\n const rawEvent = buffer.slice(0, separatorIndex);\n buffer = buffer.slice(separatorIndex + 2);\n const data = extractSseData(rawEvent);\n if (data) {\n onMessage(data);\n }\n separatorIndex = buffer.indexOf(\"\\n\\n\");\n }\n }\n } finally {\n reader.releaseLock();\n }\n}\n\nexport function createSseSubscriber({\n backendUrl,\n getHeaders,\n logger,\n}: SseSubscriberOptions): SseSubscriber {\n const subscriptions = new Map<string, SseSubscription>();\n\n const subscribe: SseSubscriber[\"subscribe\"] = (\n sessionId,\n onUpdate,\n onError,\n ) => {\n const existing = subscriptions.get(sessionId);\n const listener: SseListener = { onUpdate, onError };\n if (existing) {\n existing.listeners.add(listener);\n logger?.debug(\"[aomi][sse] listener added\", {\n sessionId,\n listeners: existing.listeners.size,\n });\n return () => {\n existing.listeners.delete(listener);\n logger?.debug(\"[aomi][sse] listener removed\", {\n sessionId,\n listeners: existing.listeners.size,\n });\n if (existing.listeners.size === 0) {\n existing.stop(\"unsubscribe\");\n if (subscriptions.get(sessionId) === existing) {\n subscriptions.delete(sessionId);\n }\n }\n };\n }\n\n const subscription: SseSubscription = {\n abortController: null,\n retries: 0,\n retryTimer: null,\n stopped: false,\n listeners: new Set([listener]),\n stop: (reason?: string) => {\n subscription.stopped = true;\n if (subscription.retryTimer) {\n clearTimeout(subscription.retryTimer);\n subscription.retryTimer = null;\n }\n subscription.abortController?.abort();\n subscription.abortController = null;\n logger?.debug(\"[aomi][sse] stop\", {\n sessionId,\n reason,\n retries: subscription.retries,\n });\n },\n };\n\n const scheduleRetry = () => {\n if (subscription.stopped) return;\n subscription.retries += 1;\n const delayMs = Math.min(500 * 2 ** (subscription.retries - 1), 10000);\n logger?.debug(\"[aomi][sse] retry scheduled\", {\n sessionId,\n delayMs,\n retries: subscription.retries,\n });\n subscription.retryTimer = setTimeout(() => {\n void open();\n }, delayMs);\n };\n\n const open = async () => {\n if (subscription.stopped) return;\n if (subscription.retryTimer) {\n clearTimeout(subscription.retryTimer);\n subscription.retryTimer = null;\n }\n\n const controller = new AbortController();\n subscription.abortController = controller;\n const openedAt = Date.now();\n\n try {\n const response = await fetch(`${backendUrl}/api/updates`, {\n headers: getHeaders(sessionId),\n signal: controller.signal,\n });\n\n if (!response.ok) {\n throw new Error(\n `SSE HTTP ${response.status}: ${response.statusText}`,\n );\n }\n\n if (!response.body) {\n throw new Error(\"SSE response missing body\");\n }\n\n subscription.retries = 0;\n\n await readSseStream(response.body, controller.signal, (data) => {\n let parsed: AomiSSEEvent;\n try {\n parsed = JSON.parse(data) as AomiSSEEvent;\n } catch (error) {\n for (const item of subscription.listeners) {\n item.onError?.(error);\n }\n return;\n }\n\n for (const item of subscription.listeners) {\n try {\n item.onUpdate(parsed);\n } catch (error) {\n item.onError?.(error);\n }\n }\n });\n logger?.debug(\"[aomi][sse] stream ended\", {\n sessionId,\n aborted: controller.signal.aborted,\n stopped: subscription.stopped,\n durationMs: Date.now() - openedAt,\n });\n } catch (error) {\n if (!controller.signal.aborted && !subscription.stopped) {\n for (const item of subscription.listeners) {\n item.onError?.(error);\n }\n }\n }\n\n if (!subscription.stopped) {\n scheduleRetry();\n }\n };\n\n subscriptions.set(sessionId, subscription);\n void open();\n\n return () => {\n subscription.listeners.delete(listener);\n logger?.debug(\"[aomi][sse] listener removed\", {\n sessionId,\n listeners: subscription.listeners.size,\n });\n if (subscription.listeners.size === 0) {\n subscription.stop(\"unsubscribe\");\n if (subscriptions.get(sessionId) === subscription) {\n subscriptions.delete(sessionId);\n }\n }\n };\n };\n\n return { subscribe };\n}\n","import type {\n AomiClientOptions,\n AomiMessage,\n AomiChatResponse,\n AomiCreateThreadResponse,\n AomiInterruptResponse,\n AomiSSEEvent,\n AomiStateResponse,\n AomiSystemEvent,\n AomiSystemResponse,\n AomiThread,\n Logger,\n UserState,\n} from \"./types\";\nimport { createSseSubscriber, type SseSubscriber } from \"./sse\";\n\n// =============================================================================\n// Internal helpers\n// =============================================================================\n\nconst SESSION_ID_HEADER = \"X-Session-Id\";\nconst API_KEY_HEADER = \"X-API-Key\";\n\nfunction toQueryString(payload: Record<string, unknown>): string {\n const params = new URLSearchParams();\n for (const [key, value] of Object.entries(payload)) {\n if (value === undefined || value === null) continue;\n params.set(key, String(value));\n }\n const qs = params.toString();\n return qs ? `?${qs}` : \"\";\n}\n\nfunction withSessionHeader(\n sessionId: string,\n init?: HeadersInit,\n): HeadersInit {\n const headers = new Headers(init);\n headers.set(SESSION_ID_HEADER, sessionId);\n return headers;\n}\n\nasync function postState<T>(\n baseUrl: string,\n path: string,\n payload: Record<string, unknown>,\n sessionId: string,\n apiKey?: string,\n): Promise<T> {\n const query = toQueryString(payload);\n const url = `${baseUrl}${path}${query}`;\n\n const headers = new Headers(withSessionHeader(sessionId));\n if (apiKey) {\n headers.set(API_KEY_HEADER, apiKey);\n }\n\n const response = await fetch(url, {\n method: \"POST\",\n headers,\n });\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n }\n\n return (await response.json()) as T;\n}\n\n// =============================================================================\n// AomiClient\n// =============================================================================\n\nexport class AomiClient {\n private readonly baseUrl: string;\n private readonly apiKey?: string;\n private readonly logger?: Logger;\n private readonly sseSubscriber: SseSubscriber;\n\n constructor(options: AomiClientOptions) {\n // Strip trailing slash\n this.baseUrl = options.baseUrl.replace(/\\/+$/, \"\");\n this.apiKey = options.apiKey;\n this.logger = options.logger;\n\n this.sseSubscriber = createSseSubscriber({\n backendUrl: this.baseUrl,\n getHeaders: (sessionId) =>\n withSessionHeader(sessionId, { Accept: \"text/event-stream\" }),\n logger: this.logger,\n });\n }\n\n // ===========================================================================\n // Chat & State\n // ===========================================================================\n\n /**\n * Fetch current session state (messages, processing status, title).\n */\n async fetchState(\n sessionId: string,\n userState?: UserState,\n ): Promise<AomiStateResponse> {\n const url = new URL(\"/api/state\", this.baseUrl);\n if (userState) {\n url.searchParams.set(\"user_state\", JSON.stringify(userState));\n }\n\n const response = await fetch(url.toString(), {\n headers: withSessionHeader(sessionId),\n });\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n }\n\n return (await response.json()) as AomiStateResponse;\n }\n\n /**\n * Send a chat message and return updated session state.\n */\n async sendMessage(\n sessionId: string,\n message: string,\n options?: {\n namespace?: string;\n publicKey?: string;\n apiKey?: string;\n userState?: UserState;\n },\n ): Promise<AomiChatResponse> {\n const namespace = options?.namespace ?? \"default\";\n const apiKey = options?.apiKey ?? this.apiKey;\n\n const payload: Record<string, unknown> = { message, namespace };\n if (options?.publicKey) {\n payload.public_key = options.publicKey;\n }\n if (options?.userState) {\n payload.user_state = JSON.stringify(options.userState);\n }\n\n return postState<AomiChatResponse>(\n this.baseUrl,\n \"/api/chat\",\n payload,\n sessionId,\n apiKey,\n );\n }\n\n /**\n * Send a system-level message (e.g. wallet state changes, context switches).\n */\n async sendSystemMessage(\n sessionId: string,\n message: string,\n ): Promise<AomiSystemResponse> {\n return postState<AomiSystemResponse>(\n this.baseUrl,\n \"/api/system\",\n { message },\n sessionId,\n );\n }\n\n /**\n * Interrupt the AI's current response.\n */\n async interrupt(sessionId: string): Promise<AomiInterruptResponse> {\n return postState<AomiInterruptResponse>(\n this.baseUrl,\n \"/api/interrupt\",\n {},\n sessionId,\n );\n }\n\n // ===========================================================================\n // SSE (Real-time Updates)\n // ===========================================================================\n\n /**\n * Subscribe to real-time SSE updates for a session.\n * Automatically reconnects with exponential backoff on disconnects.\n * Returns an unsubscribe function.\n */\n subscribeSSE(\n sessionId: string,\n onUpdate: (event: AomiSSEEvent) => void,\n onError?: (error: unknown) => void,\n ): () => void {\n return this.sseSubscriber.subscribe(sessionId, onUpdate, onError);\n }\n\n // ===========================================================================\n // Thread / Session Management\n // ===========================================================================\n\n /**\n * List all threads for a wallet address.\n */\n async listThreads(publicKey: string): Promise<AomiThread[]> {\n const url = `${this.baseUrl}/api/sessions?public_key=${encodeURIComponent(publicKey)}`;\n const response = await fetch(url);\n\n if (!response.ok) {\n throw new Error(`Failed to fetch threads: HTTP ${response.status}`);\n }\n\n return (await response.json()) as AomiThread[];\n }\n\n /**\n * Get a single thread by ID.\n */\n async getThread(sessionId: string): Promise<AomiThread> {\n const url = `${this.baseUrl}/api/sessions/${encodeURIComponent(sessionId)}`;\n const response = await fetch(url, {\n headers: withSessionHeader(sessionId),\n });\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n }\n\n return (await response.json()) as AomiThread;\n }\n\n /**\n * Create a new thread. The client generates the session ID.\n */\n async createThread(\n threadId: string,\n publicKey?: string,\n ): Promise<AomiCreateThreadResponse> {\n const body: Record<string, string> = {};\n if (publicKey) body.public_key = publicKey;\n\n const url = `${this.baseUrl}/api/sessions`;\n const response = await fetch(url, {\n method: \"POST\",\n headers: withSessionHeader(threadId, {\n \"Content-Type\": \"application/json\",\n }),\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n throw new Error(`Failed to create thread: HTTP ${response.status}`);\n }\n\n return (await response.json()) as AomiCreateThreadResponse;\n }\n\n /**\n * Delete a thread by ID.\n */\n async deleteThread(sessionId: string): Promise<void> {\n const url = `${this.baseUrl}/api/sessions/${encodeURIComponent(sessionId)}`;\n const response = await fetch(url, {\n method: \"DELETE\",\n headers: withSessionHeader(sessionId),\n });\n\n if (!response.ok) {\n throw new Error(`Failed to delete thread: HTTP ${response.status}`);\n }\n }\n\n /**\n * Rename a thread.\n */\n async renameThread(sessionId: string, newTitle: string): Promise<void> {\n const url = `${this.baseUrl}/api/sessions/${encodeURIComponent(sessionId)}`;\n const response = await fetch(url, {\n method: \"PATCH\",\n headers: withSessionHeader(sessionId, {\n \"Content-Type\": \"application/json\",\n }),\n body: JSON.stringify({ title: newTitle }),\n });\n\n if (!response.ok) {\n throw new Error(`Failed to rename thread: HTTP ${response.status}`);\n }\n }\n\n /**\n * Archive a thread.\n */\n async archiveThread(sessionId: string): Promise<void> {\n const url = `${this.baseUrl}/api/sessions/${encodeURIComponent(sessionId)}/archive`;\n const response = await fetch(url, {\n method: \"POST\",\n headers: withSessionHeader(sessionId),\n });\n\n if (!response.ok) {\n throw new Error(`Failed to archive thread: HTTP ${response.status}`);\n }\n }\n\n /**\n * Unarchive a thread.\n */\n async unarchiveThread(sessionId: string): Promise<void> {\n const url = `${this.baseUrl}/api/sessions/${encodeURIComponent(sessionId)}/unarchive`;\n const response = await fetch(url, {\n method: \"POST\",\n headers: withSessionHeader(sessionId),\n });\n\n if (!response.ok) {\n throw new Error(`Failed to unarchive thread: HTTP ${response.status}`);\n }\n }\n\n // ===========================================================================\n // System Events\n // ===========================================================================\n\n /**\n * Get system events for a session.\n */\n async getSystemEvents(\n sessionId: string,\n count?: number,\n ): Promise<AomiSystemEvent[]> {\n const url = new URL(\"/api/events\", this.baseUrl);\n if (count !== undefined) {\n url.searchParams.set(\"count\", String(count));\n }\n const response = await fetch(url.toString(), {\n headers: withSessionHeader(sessionId),\n });\n\n if (!response.ok) {\n if (response.status === 404) return [];\n throw new Error(`Failed to get system events: HTTP ${response.status}`);\n }\n\n return (await response.json()) as AomiSystemEvent[];\n }\n\n // ===========================================================================\n // Control API\n // ===========================================================================\n\n /**\n * Get available namespaces.\n */\n async getNamespaces(\n sessionId: string,\n options?: { publicKey?: string; apiKey?: string },\n ): Promise<string[]> {\n const url = new URL(\"/api/control/namespaces\", this.baseUrl);\n if (options?.publicKey) {\n url.searchParams.set(\"public_key\", options.publicKey);\n }\n\n const apiKey = options?.apiKey ?? this.apiKey;\n const headers = new Headers(withSessionHeader(sessionId));\n if (apiKey) {\n headers.set(API_KEY_HEADER, apiKey);\n }\n\n const response = await fetch(url.toString(), { headers });\n\n if (!response.ok) {\n throw new Error(`Failed to get namespaces: HTTP ${response.status}`);\n }\n\n return (await response.json()) as string[];\n }\n\n /**\n * Get available models.\n */\n async getModels(\n sessionId: string,\n options?: { apiKey?: string },\n ): Promise<string[]> {\n const url = new URL(\"/api/control/models\", this.baseUrl);\n const apiKey = options?.apiKey ?? this.apiKey;\n const headers = new Headers(withSessionHeader(sessionId));\n if (apiKey) {\n headers.set(API_KEY_HEADER, apiKey);\n }\n\n const response = await fetch(url.toString(), {\n headers,\n });\n\n if (!response.ok) {\n throw new Error(`Failed to get models: HTTP ${response.status}`);\n }\n\n return (await response.json()) as string[];\n }\n\n /**\n * Set the model for a session.\n */\n async setModel(\n sessionId: string,\n rig: string,\n options?: { namespace?: string; apiKey?: string },\n ): Promise<{\n success: boolean;\n rig: string;\n baml: string;\n created: boolean;\n }> {\n const apiKey = options?.apiKey ?? this.apiKey;\n const payload: Record<string, unknown> = { rig };\n if (options?.namespace) {\n payload.namespace = options.namespace;\n }\n\n return postState<{\n success: boolean;\n rig: string;\n baml: string;\n created: boolean;\n }>(this.baseUrl, \"/api/control/model\", payload, sessionId, apiKey);\n }\n}\n","// =============================================================================\n// User State\n// =============================================================================\n\n/**\n * Client-side user state synced with the backend.\n * Typically wallet connection info, but can be any key-value data.\n */\nexport type UserState = Record<string, unknown>;\n\n// =============================================================================\n// Logger\n// =============================================================================\n\n/**\n * Optional logger for debug output. Pass `console` or any compatible object.\n */\nexport type Logger = {\n debug: (...args: unknown[]) => void;\n};\n\n// =============================================================================\n// Client Options\n// =============================================================================\n\nexport type AomiClientOptions = {\n /** Base URL of the Aomi backend (e.g. \"https://aomi.dev\") */\n baseUrl: string;\n /** Default API key for non-default namespaces */\n apiKey?: string;\n /** Optional logger for debug output (default: silent) */\n logger?: Logger;\n};\n\n// =============================================================================\n// Base Types\n// =============================================================================\n\nexport interface AomiMessage {\n sender?: \"user\" | \"agent\" | \"system\" | string;\n content?: string;\n timestamp?: string;\n is_streaming?: boolean;\n tool_result?: [string, string] | null;\n}\n\n// =============================================================================\n// API Response Types\n// =============================================================================\n\n/**\n * GET /api/state\n * Fetches current session state including messages and processing status\n */\nexport interface AomiStateResponse {\n messages?: AomiMessage[] | null;\n system_events?: AomiSystemEvent[] | null;\n title?: string | null;\n is_processing?: boolean;\n}\n\n/**\n * POST /api/chat\n * Sends a chat message and returns updated session state\n */\nexport interface AomiChatResponse {\n messages?: AomiMessage[] | null;\n system_events?: AomiSystemEvent[] | null;\n title?: string | null;\n is_processing?: boolean;\n}\n\n/**\n * POST /api/system\n * Sends a system message and returns the response message\n */\nexport interface AomiSystemResponse {\n res?: AomiMessage | null;\n}\n\n/**\n * POST /api/interrupt\n * Interrupts current processing and returns updated session state\n */\nexport type AomiInterruptResponse = AomiChatResponse;\n\n/**\n * GET /api/sessions\n * Returns array of AomiThread\n */\nexport interface AomiThread {\n session_id: string;\n title: string;\n is_archived?: boolean;\n}\n\n/**\n * POST /api/sessions\n * Creates a new thread/session\n */\nexport interface AomiCreateThreadResponse {\n session_id: string;\n title?: string;\n}\n\n// =============================================================================\n// SSE Event Types (/api/updates)\n// =============================================================================\n\n/**\n * Base SSE event - all events have session_id and type\n */\nexport type AomiSSEEvent = {\n type:\n | \"title_changed\"\n | \"tool_update\"\n | \"tool_complete\"\n | \"system_notice\"\n | string;\n session_id: string;\n new_title?: string;\n [key: string]: unknown;\n};\n\nexport type AomiSSEEventType =\n | \"title_changed\"\n | \"tool_update\"\n | \"tool_complete\"\n | \"system_notice\";\n\n// =============================================================================\n// System Events (/api/events)\n// =============================================================================\n\n/**\n * Backend SystemEvent enum serializes as tagged JSON:\n * - InlineCall: {\"InlineCall\": {\"type\": \"wallet_tx_request\", \"payload\": {...}}}\n * - SystemNotice: {\"SystemNotice\": \"message\"}\n * - SystemError: {\"SystemError\": \"message\"}\n * - AsyncCallback: {\"AsyncCallback\": {...}} (not sent over HTTP)\n */\nexport type AomiSystemEvent =\n | { InlineCall: { type: string; payload?: unknown; [key: string]: unknown } }\n | { SystemNotice: string }\n | { SystemError: string }\n | { AsyncCallback: Record<string, unknown> };\n\n// =============================================================================\n// Type Guards\n// =============================================================================\n\nexport function isInlineCall(\n event: AomiSystemEvent,\n): event is { InlineCall: { type: string; payload?: unknown } } {\n return \"InlineCall\" in event;\n}\n\nexport function isSystemNotice(\n event: AomiSystemEvent,\n): event is { SystemNotice: string } {\n return \"SystemNotice\" in event;\n}\n\nexport function isSystemError(\n event: AomiSystemEvent,\n): event is { SystemError: string } {\n return \"SystemError\" in event;\n}\n\nexport function isAsyncCallback(\n event: AomiSystemEvent,\n): event is { AsyncCallback: Record<string, unknown> } {\n return \"AsyncCallback\" in event;\n}\n","// =============================================================================\n// Typed EventEmitter (browser-safe, no Node.js deps)\n// =============================================================================\n\ntype Listener<T = unknown> = (payload: T) => void;\n\n/**\n * Minimal typed event emitter with wildcard support.\n *\n * ```ts\n * type Events = { message: string; error: { code: number } };\n * const ee = new TypedEventEmitter<Events>();\n * ee.on(\"message\", (msg) => console.log(msg));\n * ee.emit(\"message\", \"hello\");\n * ```\n */\nexport class TypedEventEmitter<\n EventMap extends Record<string, unknown> = Record<string, unknown>,\n> {\n private listeners = new Map<string, Set<Listener<never>>>();\n\n /**\n * Subscribe to an event type. Returns an unsubscribe function.\n */\n on<K extends keyof EventMap & string>(\n type: K,\n handler: Listener<EventMap[K]>,\n ): () => void {\n let set = this.listeners.get(type);\n if (!set) {\n set = new Set();\n this.listeners.set(type, set);\n }\n set.add(handler as Listener<never>);\n\n return () => {\n set!.delete(handler as Listener<never>);\n if (set!.size === 0) {\n this.listeners.delete(type);\n }\n };\n }\n\n /**\n * Subscribe to an event type for a single emission, then auto-unsubscribe.\n */\n once<K extends keyof EventMap & string>(\n type: K,\n handler: Listener<EventMap[K]>,\n ): () => void {\n const wrapper = ((payload: EventMap[K]) => {\n unsub();\n handler(payload);\n }) as Listener<EventMap[K]>;\n\n const unsub = this.on(type, wrapper);\n return unsub;\n }\n\n /**\n * Emit an event to all listeners of `type` and wildcard `\"*\"` listeners.\n */\n emit<K extends keyof EventMap & string>(\n type: K,\n payload: EventMap[K],\n ): void {\n // Type-specific listeners\n const typeSet = this.listeners.get(type);\n if (typeSet) {\n for (const handler of typeSet) {\n (handler as Listener<EventMap[K]>)(payload);\n }\n }\n\n // Wildcard listeners\n if (type !== \"*\") {\n const wildcardSet = this.listeners.get(\"*\");\n if (wildcardSet) {\n for (const handler of wildcardSet) {\n (handler as Listener<unknown>)({ type, payload });\n }\n }\n }\n }\n\n /**\n * Remove a specific handler from an event type.\n */\n off<K extends keyof EventMap & string>(\n type: K,\n handler: Listener<EventMap[K]>,\n ): void {\n const set = this.listeners.get(type);\n if (set) {\n set.delete(handler as Listener<never>);\n if (set.size === 0) {\n this.listeners.delete(type);\n }\n }\n }\n\n /**\n * Remove all listeners for all event types.\n */\n removeAllListeners(): void {\n this.listeners.clear();\n }\n}\n","// =============================================================================\n// System Event Unwrap\n// =============================================================================\n//\n// Converts tagged-enum AomiSystemEvent into a flat { type, payload } object.\n// Ported from packages/react/src/contexts/event-context.tsx dispatchSystemEvents.\n\nimport type { AomiSystemEvent } from \"./types\";\nimport {\n isInlineCall,\n isSystemNotice,\n isSystemError,\n isAsyncCallback,\n} from \"./types\";\n\nexport type UnwrappedEvent = {\n type: string;\n payload: unknown;\n};\n\n/**\n * Unwrap a tagged-enum AomiSystemEvent from the backend into a flat event.\n *\n * ```ts\n * const event: AomiSystemEvent = { InlineCall: { type: \"wallet_tx_request\", payload: { to: \"0x...\" } } };\n * const unwrapped = unwrapSystemEvent(event);\n * // => { type: \"wallet_tx_request\", payload: { to: \"0x...\" } }\n * ```\n */\nexport function unwrapSystemEvent(\n event: AomiSystemEvent,\n): UnwrappedEvent | null {\n if (isInlineCall(event)) {\n return {\n type: event.InlineCall.type,\n payload: event.InlineCall.payload ?? event.InlineCall,\n };\n }\n\n if (isSystemNotice(event)) {\n return {\n type: \"system_notice\",\n payload: { message: event.SystemNotice },\n };\n }\n\n if (isSystemError(event)) {\n return {\n type: \"system_error\",\n payload: { message: event.SystemError },\n };\n }\n\n if (isAsyncCallback(event)) {\n return {\n type: \"async_callback\",\n payload: event.AsyncCallback,\n };\n }\n\n return null;\n}\n","// =============================================================================\n// Wallet Payload Normalization\n// =============================================================================\n//\n// Pure functions extracted from packages/react/src/handlers/wallet-handler.ts.\n// Normalizes the various payload shapes the backend can send for wallet\n// transaction and EIP-712 signing requests.\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport type WalletTxPayload = {\n to: string;\n value?: string;\n data?: string;\n chainId?: number;\n};\n\nexport type WalletEip712Payload = {\n typed_data?: {\n domain?: { chainId?: number | string };\n types?: Record<string, Array<{ name: string; type: string }>>;\n primaryType?: string;\n message?: Record<string, unknown>;\n };\n description?: string;\n};\n\n// =============================================================================\n// Helpers\n// =============================================================================\n\ntype UnknownRecord = Record<string, unknown>;\n\nfunction asRecord(value: unknown): UnknownRecord | undefined {\n if (!value || typeof value !== \"object\" || Array.isArray(value))\n return undefined;\n return value as UnknownRecord;\n}\n\nfunction getToolArgs(payload: unknown): UnknownRecord {\n const root = asRecord(payload);\n const nestedArgs = asRecord(root?.args);\n return nestedArgs ?? root ?? {};\n}\n\nfunction parseChainId(value: unknown): number | undefined {\n if (typeof value === \"number\" && Number.isFinite(value)) return value;\n if (typeof value !== \"string\") return undefined;\n\n const trimmed = value.trim();\n if (!trimmed) return undefined;\n\n if (trimmed.startsWith(\"0x\")) {\n const parsedHex = Number.parseInt(trimmed.slice(2), 16);\n return Number.isFinite(parsedHex) ? parsedHex : undefined;\n }\n\n const parsed = Number.parseInt(trimmed, 10);\n return Number.isFinite(parsed) ? parsed : undefined;\n}\n\n// =============================================================================\n// Normalization\n// =============================================================================\n\n/**\n * Normalize a wallet_tx_request payload into a consistent shape.\n * Returns `null` if the payload is missing the required `to` field.\n */\nexport function normalizeTxPayload(payload: unknown): WalletTxPayload | null {\n const root = asRecord(payload);\n const args = getToolArgs(payload);\n const ctx = asRecord(root?.ctx);\n\n const to = typeof args.to === \"string\" ? args.to : undefined;\n if (!to) return null;\n\n const valueRaw = args.value;\n const value =\n typeof valueRaw === \"string\"\n ? valueRaw\n : typeof valueRaw === \"number\" && Number.isFinite(valueRaw)\n ? String(Math.trunc(valueRaw))\n : undefined;\n\n const data = typeof args.data === \"string\" ? args.data : undefined;\n const chainId =\n parseChainId(args.chainId) ??\n parseChainId(args.chain_id) ??\n parseChainId(ctx?.user_chain_id) ??\n parseChainId(ctx?.userChainId);\n\n return { to, value, data, chainId };\n}\n\n/**\n * Normalize an EIP-712 signing request payload.\n */\nexport function normalizeEip712Payload(\n payload: unknown,\n): WalletEip712Payload {\n const args = getToolArgs(payload);\n const typedDataRaw = args.typed_data ?? args.typedData;\n let typedData: WalletEip712Payload[\"typed_data\"] | undefined;\n\n if (typeof typedDataRaw === \"string\") {\n try {\n const parsed = JSON.parse(typedDataRaw) as unknown;\n if (parsed && typeof parsed === \"object\" && !Array.isArray(parsed)) {\n typedData = parsed as WalletEip712Payload[\"typed_data\"];\n }\n } catch {\n typedData = undefined;\n }\n } else if (\n typedDataRaw &&\n typeof typedDataRaw === \"object\" &&\n !Array.isArray(typedDataRaw)\n ) {\n typedData = typedDataRaw as WalletEip712Payload[\"typed_data\"];\n }\n\n const description =\n typeof args.description === \"string\" ? args.description : undefined;\n\n return { typed_data: typedData, description };\n}\n","// =============================================================================\n// Session — High-level orchestrated client\n// =============================================================================\n//\n// Wraps AomiClient with polling, event dispatch, and wallet request management.\n// Ported from the React runtime (polling-controller, event-context, wallet-handler).\n//\n// Usage:\n// const session = new Session({ baseUrl: \"https://api.aomi.dev\" });\n// session.on(\"wallet_tx_request\", async (req) => {\n// const signed = await signer.signTransaction(req.payload);\n// await session.resolve(req.id, { txHash: signed.hash });\n// });\n// const result = await session.send(\"swap 1 ETH for USDC\");\n// session.close();\n\nimport { AomiClient } from \"./client\";\nimport type {\n AomiClientOptions,\n AomiMessage,\n AomiChatResponse,\n AomiSSEEvent,\n AomiStateResponse,\n AomiSystemEvent,\n UserState,\n} from \"./types\";\nimport { TypedEventEmitter } from \"./event-emitter\";\nimport { unwrapSystemEvent } from \"./event-unwrap\";\nimport {\n normalizeTxPayload,\n normalizeEip712Payload,\n type WalletTxPayload,\n type WalletEip712Payload,\n} from \"./wallet-utils\";\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport type WalletRequestKind = \"transaction\" | \"eip712_sign\";\n\nexport type WalletRequest = {\n id: string;\n kind: WalletRequestKind;\n payload: WalletTxPayload | WalletEip712Payload;\n timestamp: number;\n};\n\nexport type WalletRequestResult = {\n txHash?: string;\n signature?: string;\n amount?: string;\n};\n\nexport type SendResult = {\n messages: AomiMessage[];\n title?: string;\n};\n\nexport type SessionOptions = {\n /** Session ID. Auto-generated (crypto.randomUUID) if omitted. */\n sessionId?: string;\n /** Namespace for chat messages. Default: \"default\" */\n namespace?: string;\n /** User public key (wallet address). */\n publicKey?: string;\n /** API key override. */\n apiKey?: string;\n /** User state to send with requests (wallet connection info, etc). */\n userState?: UserState;\n /** Polling interval in ms. Default: 500 */\n pollIntervalMs?: number;\n /** Logger for debug output. Pass `console` for verbose logging. */\n logger?: { debug: (...args: unknown[]) => void };\n};\n\n/** Events emitted by Session. */\nexport type SessionEventMap = {\n /** A transaction signing request arrived from the backend. */\n wallet_tx_request: WalletRequest;\n /** An EIP-712 signing request arrived from the backend. */\n wallet_eip712_request: WalletRequest;\n /** A system notice from the backend. */\n system_notice: { message: string };\n /** A system error from the backend. */\n system_error: { message: string };\n /** An async callback event. */\n async_callback: Record<string, unknown>;\n /** SSE: tool execution in progress. */\n tool_update: AomiSSEEvent;\n /** SSE: tool execution completed. */\n tool_complete: AomiSSEEvent;\n /** Session title changed. */\n title_changed: { title: string };\n /** Messages updated (new messages from poll or send response). */\n messages: AomiMessage[];\n /** AI started processing. */\n processing_start: undefined;\n /** AI finished processing. */\n processing_end: undefined;\n /** An error occurred during polling or SSE. */\n error: { error: unknown };\n /** Wildcard: receives all events as { type, payload }. */\n \"*\": { type: string; payload: unknown };\n};\n\n// =============================================================================\n// Session Class\n// =============================================================================\n\nexport class Session extends TypedEventEmitter<SessionEventMap> {\n /** The underlying low-level client. */\n readonly client: AomiClient;\n /** The session (thread) ID. */\n readonly sessionId: string;\n\n private namespace: string;\n private publicKey?: string;\n private apiKey?: string;\n private userState?: UserState;\n private pollIntervalMs: number;\n private logger?: { debug: (...args: unknown[]) => void };\n\n // Internal state\n private pollTimer: ReturnType<typeof setInterval> | null = null;\n private unsubscribeSSE: (() => void) | null = null;\n private _isProcessing = false;\n private walletRequests: WalletRequest[] = [];\n private walletRequestNextId = 1;\n private _messages: AomiMessage[] = [];\n private _title?: string;\n private closed = false;\n\n // For send() blocking behavior\n private pendingResolve: ((result: SendResult) => void) | null = null;\n\n constructor(\n clientOrOptions: AomiClient | AomiClientOptions,\n sessionOptions?: SessionOptions,\n ) {\n super();\n\n this.client =\n clientOrOptions instanceof AomiClient\n ? clientOrOptions\n : new AomiClient(clientOrOptions);\n\n this.sessionId = sessionOptions?.sessionId ?? crypto.randomUUID();\n this.namespace = sessionOptions?.namespace ?? \"default\";\n this.publicKey = sessionOptions?.publicKey;\n this.apiKey = sessionOptions?.apiKey;\n this.userState = sessionOptions?.userState;\n this.pollIntervalMs = sessionOptions?.pollIntervalMs ?? 500;\n this.logger = sessionOptions?.logger;\n\n // Start SSE subscription\n this.unsubscribeSSE = this.client.subscribeSSE(\n this.sessionId,\n (event) => this.handleSSEEvent(event),\n (error) => this.emit(\"error\", { error }),\n );\n }\n\n // ===========================================================================\n // Public API — Chat\n // ===========================================================================\n\n /**\n * Send a message and wait for the AI to finish processing.\n *\n * The returned promise resolves when `is_processing` becomes `false` AND\n * there are no pending wallet requests. If a wallet request arrives\n * mid-processing, polling continues but the promise pauses until the\n * request is resolved or rejected via `resolve()` / `reject()`.\n */\n async send(message: string): Promise<SendResult> {\n this.assertOpen();\n\n const response = await this.client.sendMessage(this.sessionId, message, {\n namespace: this.namespace,\n publicKey: this.publicKey,\n apiKey: this.apiKey,\n userState: this.userState,\n });\n\n this.applyState(response);\n\n if (!response.is_processing && this.walletRequests.length === 0) {\n return { messages: this._messages, title: this._title };\n }\n\n this._isProcessing = true;\n this.emit(\"processing_start\", undefined);\n\n return new Promise<SendResult>((resolve) => {\n this.pendingResolve = resolve;\n this.startPolling();\n });\n }\n\n /**\n * Send a message without waiting for completion.\n * Polling starts in the background; listen to events for updates.\n */\n async sendAsync(message: string): Promise<AomiChatResponse> {\n this.assertOpen();\n\n const response = await this.client.sendMessage(this.sessionId, message, {\n namespace: this.namespace,\n publicKey: this.publicKey,\n apiKey: this.apiKey,\n userState: this.userState,\n });\n\n this.applyState(response);\n\n if (response.is_processing) {\n this._isProcessing = true;\n this.emit(\"processing_start\", undefined);\n this.startPolling();\n }\n\n return response;\n }\n\n // ===========================================================================\n // Public API — Wallet Request Resolution\n // ===========================================================================\n\n /**\n * Resolve a pending wallet request (transaction or EIP-712 signing).\n * Sends the result to the backend and resumes polling.\n */\n async resolve(requestId: string, result: WalletRequestResult): Promise<void> {\n const req = this.removeWalletRequest(requestId);\n if (!req) {\n throw new Error(`No pending wallet request with id \"${requestId}\"`);\n }\n\n if (req.kind === \"transaction\") {\n await this.sendSystemEvent(\"wallet:tx_complete\", {\n txHash: result.txHash ?? \"\",\n status: \"success\",\n amount: result.amount,\n });\n } else {\n const eip712Payload = req.payload as WalletEip712Payload;\n await this.sendSystemEvent(\"wallet_eip712_response\", {\n status: \"success\",\n signature: result.signature,\n description: eip712Payload.description,\n });\n }\n\n // Resume polling if still processing\n if (this._isProcessing) {\n this.startPolling();\n }\n }\n\n /**\n * Reject a pending wallet request.\n * Sends an error to the backend and resumes polling.\n */\n async reject(requestId: string, reason?: string): Promise<void> {\n const req = this.removeWalletRequest(requestId);\n if (!req) {\n throw new Error(`No pending wallet request with id \"${requestId}\"`);\n }\n\n if (req.kind === \"transaction\") {\n await this.sendSystemEvent(\"wallet:tx_complete\", {\n txHash: \"\",\n status: \"failed\",\n });\n } else {\n const eip712Payload = req.payload as WalletEip712Payload;\n await this.sendSystemEvent(\"wallet_eip712_response\", {\n status: \"failed\",\n error: reason ?? \"Request rejected\",\n description: eip712Payload.description,\n });\n }\n\n if (this._isProcessing) {\n this.startPolling();\n }\n }\n\n // ===========================================================================\n // Public API — Control\n // ===========================================================================\n\n /**\n * Cancel the AI's current response.\n */\n async interrupt(): Promise<void> {\n this.stopPolling();\n const response = await this.client.interrupt(this.sessionId);\n this.applyState(response);\n this._isProcessing = false;\n this.emit(\"processing_end\", undefined);\n this.resolvePending();\n }\n\n /**\n * Close the session. Stops polling, unsubscribes SSE, removes all listeners.\n * The session cannot be used after closing.\n */\n close(): void {\n if (this.closed) return;\n this.closed = true;\n this.stopPolling();\n this.unsubscribeSSE?.();\n this.unsubscribeSSE = null;\n this.resolvePending();\n this.removeAllListeners();\n }\n\n // ===========================================================================\n // Public API — Accessors\n // ===========================================================================\n\n /** Current messages in the session. */\n getMessages(): AomiMessage[] {\n return this._messages;\n }\n\n /** Current session title. */\n getTitle(): string | undefined {\n return this._title;\n }\n\n /** Pending wallet requests waiting for resolve/reject. */\n getPendingRequests(): WalletRequest[] {\n return [...this.walletRequests];\n }\n\n /** Whether the AI is currently processing. */\n getIsProcessing(): boolean {\n return this._isProcessing;\n }\n\n // ===========================================================================\n // Internal — Polling (ported from PollingController)\n // ===========================================================================\n\n private startPolling(): void {\n if (this.pollTimer || this.closed) return;\n\n this.logger?.debug(\"[session] polling started\", this.sessionId);\n this.pollTimer = setInterval(() => {\n void this.pollTick();\n }, this.pollIntervalMs);\n }\n\n private stopPolling(): void {\n if (this.pollTimer) {\n clearInterval(this.pollTimer);\n this.pollTimer = null;\n this.logger?.debug(\"[session] polling stopped\", this.sessionId);\n }\n }\n\n private async pollTick(): Promise<void> {\n if (!this.pollTimer) return;\n\n try {\n const state = await this.client.fetchState(\n this.sessionId,\n this.userState,\n );\n\n // Guard: polling may have been stopped while awaiting fetch\n if (!this.pollTimer) return;\n\n this.applyState(state);\n\n if (!state.is_processing && this.walletRequests.length === 0) {\n this.stopPolling();\n this._isProcessing = false;\n this.emit(\"processing_end\", undefined);\n this.resolvePending();\n }\n } catch (error) {\n this.logger?.debug(\"[session] poll error\", error);\n this.emit(\"error\", { error });\n }\n }\n\n // ===========================================================================\n // Internal — State Application\n // ===========================================================================\n\n private applyState(\n state: Pick<\n AomiStateResponse,\n \"messages\" | \"system_events\" | \"title\" | \"is_processing\"\n >,\n ): void {\n if (state.messages) {\n this._messages = state.messages;\n this.emit(\"messages\", this._messages);\n }\n\n if (state.title) {\n this._title = state.title;\n }\n\n if (state.system_events?.length) {\n this.dispatchSystemEvents(state.system_events);\n }\n }\n\n private dispatchSystemEvents(events: AomiSystemEvent[]): void {\n for (const event of events) {\n const unwrapped = unwrapSystemEvent(event);\n if (!unwrapped) continue;\n\n if (unwrapped.type === \"wallet_tx_request\") {\n const payload = normalizeTxPayload(unwrapped.payload);\n if (payload) {\n const req = this.enqueueWalletRequest(\"transaction\", payload);\n this.emit(\"wallet_tx_request\", req);\n }\n } else if (unwrapped.type === \"wallet_eip712_request\") {\n const payload = normalizeEip712Payload(unwrapped.payload ?? {});\n const req = this.enqueueWalletRequest(\"eip712_sign\", payload);\n this.emit(\"wallet_eip712_request\", req);\n } else if (\n unwrapped.type === \"system_notice\" ||\n unwrapped.type === \"system_error\" ||\n unwrapped.type === \"async_callback\"\n ) {\n // These match known event map keys — emit directly\n this.emit(\n unwrapped.type as keyof SessionEventMap,\n unwrapped.payload as never,\n );\n }\n }\n }\n\n // ===========================================================================\n // Internal — SSE Handling\n // ===========================================================================\n\n private handleSSEEvent(event: AomiSSEEvent): void {\n if (event.type === \"title_changed\" && event.new_title) {\n this._title = event.new_title;\n this.emit(\"title_changed\", { title: event.new_title });\n } else if (event.type === \"tool_update\") {\n this.emit(\"tool_update\", event);\n } else if (event.type === \"tool_complete\") {\n this.emit(\"tool_complete\", event);\n }\n }\n\n // ===========================================================================\n // Internal — Wallet Request Queue\n // ===========================================================================\n\n private enqueueWalletRequest(\n kind: WalletRequestKind,\n payload: WalletTxPayload | WalletEip712Payload,\n ): WalletRequest {\n const req: WalletRequest = {\n id: `wreq-${this.walletRequestNextId++}`,\n kind,\n payload,\n timestamp: Date.now(),\n };\n this.walletRequests.push(req);\n return req;\n }\n\n private removeWalletRequest(id: string): WalletRequest | null {\n const idx = this.walletRequests.findIndex((r) => r.id === id);\n if (idx === -1) return null;\n return this.walletRequests.splice(idx, 1)[0];\n }\n\n // ===========================================================================\n // Internal — Helpers\n // ===========================================================================\n\n private async sendSystemEvent(\n type: string,\n payload: unknown,\n ): Promise<void> {\n const message = JSON.stringify({ type, payload });\n await this.client.sendSystemMessage(this.sessionId, message);\n }\n\n private resolvePending(): void {\n if (this.pendingResolve) {\n const resolve = this.pendingResolve;\n this.pendingResolve = null;\n resolve({ messages: this._messages, title: this._title });\n }\n }\n\n private assertOpen(): void {\n if (this.closed) {\n throw new Error(\"Session is closed\");\n }\n }\n}\n"],"mappings":"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","names":["_a"]}
|
package/package.json
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aomi-labs/client",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Platform-agnostic TypeScript client for the Aomi backend API",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
7
7
|
"module": "./dist/index.js",
|
|
8
8
|
"types": "./dist/index.d.ts",
|
|
9
|
+
"bin": {
|
|
10
|
+
"aomi": "./dist/cli.js"
|
|
11
|
+
},
|
|
9
12
|
"exports": {
|
|
10
13
|
".": {
|
|
11
14
|
"import": {
|
|
@@ -19,8 +22,13 @@
|
|
|
19
22
|
}
|
|
20
23
|
},
|
|
21
24
|
"files": [
|
|
22
|
-
"dist"
|
|
25
|
+
"dist",
|
|
26
|
+
"skills",
|
|
27
|
+
"README.md"
|
|
23
28
|
],
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"viem": "^2.40.3"
|
|
31
|
+
},
|
|
24
32
|
"scripts": {
|
|
25
33
|
"build": "tsup",
|
|
26
34
|
"clean:dist": "rm -rf dist"
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: aomi-transact
|
|
3
|
+
description: >
|
|
4
|
+
Interact with aomi AI transaction builder via CLI. Use when the user asks to
|
|
5
|
+
swap tokens, send transactions, check token prices, sign transactions, manage
|
|
6
|
+
wallet operations, or interact with DeFi protocols on-chain. Aomi's runtime
|
|
7
|
+
builds generic EVM transactions with speed and security natively on Ethereum
|
|
8
|
+
light client. Handles multi-step workflows: chat with the agent, review pending
|
|
9
|
+
transactions, sign and broadcast, then verify results.
|
|
10
|
+
compatibility: "Requires @aomi-labs/client (`npm install -g @aomi-labs/client`). CLI executable is `aomi`. Set PRIVATE_KEY and CHAIN_RPC_URL for transaction signing."
|
|
11
|
+
license: MIT
|
|
12
|
+
allowed-tools: Bash
|
|
13
|
+
metadata:
|
|
14
|
+
author: aomi-labs
|
|
15
|
+
version: "0.1"
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
# Aomi On-Chain Agent
|
|
19
|
+
|
|
20
|
+
Build and execute EVM transactions through a conversational AI agent.
|
|
21
|
+
|
|
22
|
+
## Setup
|
|
23
|
+
|
|
24
|
+
Before running any commands, verify the CLI is available:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
which aomi || npm install -g @aomi-labs/client
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Core Workflow
|
|
31
|
+
|
|
32
|
+
The aomi CLI is **not** a long-running process. Each command starts, runs, and
|
|
33
|
+
exits. Conversation history lives on the backend. Local state (session ID,
|
|
34
|
+
pending/signed txs) is persisted to `$TMPDIR/aomi-session.json`.
|
|
35
|
+
|
|
36
|
+
The typical flow is:
|
|
37
|
+
|
|
38
|
+
1. **Chat** — send a message, the agent responds (and may queue transactions)
|
|
39
|
+
2. **Review** — list pending transactions
|
|
40
|
+
3. **Sign** — sign and broadcast a pending transaction
|
|
41
|
+
4. **Verify** — check signed transactions or continue chatting
|
|
42
|
+
|
|
43
|
+
## Commands
|
|
44
|
+
|
|
45
|
+
### Chat with the agent
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
aomi chat "<message>"
|
|
49
|
+
aomi chat "<message>" --verbose # stream tool calls + responses live
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Always quote the message. Use `--verbose` (or `-v`) to see real-time tool calls,
|
|
53
|
+
agent reasoning, and intermediate results.
|
|
54
|
+
|
|
55
|
+
If the agent builds a transaction, it prints a wallet request notice:
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
⚡ Wallet request queued: tx-1
|
|
59
|
+
to: 0x3fC91A3afd70395Cd496C647d5a6CC9D4B2b7FAD
|
|
60
|
+
value: 1000000000000000000
|
|
61
|
+
chain: 1
|
|
62
|
+
Run `aomi tx` to see pending transactions, `aomi sign <id>` to sign.
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Connect a wallet
|
|
66
|
+
|
|
67
|
+
Pass `--public-key` on the first chat so the agent knows the user's wallet
|
|
68
|
+
address. It is persisted — subsequent commands in the same session don't need it.
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
aomi chat "swap 1 ETH for USDC" --public-key 0xYourAddress
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### List transactions
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
aomi tx
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Shows pending (unsigned) and signed (completed) transactions with IDs, targets,
|
|
81
|
+
values, and timestamps.
|
|
82
|
+
|
|
83
|
+
### Sign a transaction
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
aomi sign <tx-id> --private-key <hex-key> --rpc-url <rpc-url>
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Signs the pending transaction, broadcasts it on-chain, and notifies the backend.
|
|
90
|
+
Supports both regular transactions and EIP-712 typed data signatures (e.g. for
|
|
91
|
+
CoW Protocol orders, permit approvals).
|
|
92
|
+
|
|
93
|
+
### View conversation log
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
aomi log
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Replays all messages with timestamps, tool results, and agent responses.
|
|
100
|
+
|
|
101
|
+
### Session management
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
aomi status # session info (ID, message count, pending/signed tx counts)
|
|
105
|
+
aomi events # system events from the backend
|
|
106
|
+
aomi close # wipe local state, start fresh on next chat
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Configuration
|
|
110
|
+
|
|
111
|
+
All config can be passed as flags (priority) or env vars (fallback):
|
|
112
|
+
|
|
113
|
+
| Flag | Env Variable | Default | Description |
|
|
114
|
+
|------|-------------|---------|-------------|
|
|
115
|
+
| `--backend-url` | `AOMI_BASE_URL` | `https://api.aomi.dev` | Backend URL |
|
|
116
|
+
| `--api-key` | `AOMI_API_KEY` | — | API key for non-default namespaces |
|
|
117
|
+
| `--namespace` | `AOMI_NAMESPACE` | `default` | Namespace |
|
|
118
|
+
| `--public-key` | `AOMI_PUBLIC_KEY` | — | Wallet address |
|
|
119
|
+
| `--private-key` | `PRIVATE_KEY` | — | Hex private key (for `aomi sign`) |
|
|
120
|
+
| `--rpc-url` | `CHAIN_RPC_URL` | — | RPC URL (for `aomi sign`) |
|
|
121
|
+
|
|
122
|
+
## Important Behavior
|
|
123
|
+
|
|
124
|
+
- **Session continuity**: After the first `aomi chat`, the session ID is saved.
|
|
125
|
+
All subsequent commands operate on the same conversation until `aomi close`.
|
|
126
|
+
- **Transaction IDs**: Each wallet request gets a unique ID (`tx-1`, `tx-2`, ...).
|
|
127
|
+
Use the exact ID shown in `aomi tx` when signing.
|
|
128
|
+
- **EIP-712**: The agent may request typed data signatures (e.g. `kind: eip712_sign`)
|
|
129
|
+
for gasless swaps or permit approvals. `aomi sign` handles both kinds automatically.
|
|
130
|
+
- **Non-blocking**: `aomi chat` exits as soon as the agent finishes responding or
|
|
131
|
+
a wallet request arrives. It does not wait for signing.
|
|
132
|
+
- **Verbose mode**: Always use `--verbose` when you need to see what tools the
|
|
133
|
+
agent is calling or debug unexpected behavior.
|
|
134
|
+
|
|
135
|
+
## Example: Full Swap Flow
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
# 1. Start a session with wallet connected
|
|
139
|
+
aomi chat "swap 1 ETH for USDC on Uniswap" \
|
|
140
|
+
--public-key 0xYourAddress
|
|
141
|
+
|
|
142
|
+
# 2. Agent builds the tx — check what's pending
|
|
143
|
+
aomi tx
|
|
144
|
+
|
|
145
|
+
# 3. Sign and broadcast
|
|
146
|
+
aomi sign tx-1 \
|
|
147
|
+
--private-key 0xYourPrivateKey \
|
|
148
|
+
--rpc-url https://eth.llamarpc.com
|
|
149
|
+
|
|
150
|
+
# 4. Verify
|
|
151
|
+
aomi tx # should show tx-1 under "Signed" with hash
|
|
152
|
+
aomi log # full conversation replay
|
|
153
|
+
|
|
154
|
+
# 5. Clean up when done
|
|
155
|
+
aomi close
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## Error Handling
|
|
159
|
+
|
|
160
|
+
- If `aomi chat` returns "(no response)", the agent may still be processing.
|
|
161
|
+
Wait a moment and run `aomi status` to check.
|
|
162
|
+
- If a transaction fails on-chain, the error message from the RPC is printed.
|
|
163
|
+
Check the RPC URL, gas, and account balance.
|
|
164
|
+
- Run `aomi close` to reset if the session gets into a bad state.
|