@mantyx/sdk 0.1.0
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/CHANGELOG.md +35 -0
- package/CONTRIBUTING.md +78 -0
- package/EXTRACT.md +70 -0
- package/LICENSE +201 -0
- package/README.md +304 -0
- package/dist/index.cjs +650 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +346 -0
- package/dist/index.d.ts +346 -0
- package/dist/index.js +608 -0
- package/dist/index.js.map +1 -0
- package/docs/agent-runs-protocol.md +384 -0
- package/package.json +59 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/errors.ts","../src/sse.ts","../src/tools.ts","../src/zod-to-json-schema.ts","../src/client.ts","../src/version.ts"],"sourcesContent":["/**\n * Error types raised by the MANTYX SDK.\n */\n\nexport class MantyxError extends Error {\n readonly code: string;\n readonly status: number | undefined;\n readonly hint: string | undefined;\n\n constructor(\n message: string,\n opts: { code?: string; status?: number; hint?: string } = {},\n ) {\n super(message);\n this.name = \"MantyxError\";\n this.code = opts.code ?? \"mantyx_error\";\n this.status = opts.status;\n this.hint = opts.hint;\n }\n}\n\nexport class MantyxNetworkError extends MantyxError {\n constructor(message: string, opts: { cause?: unknown } = {}) {\n super(message, { code: \"network\" });\n this.name = \"MantyxNetworkError\";\n if (opts.cause !== undefined) {\n (this as Error & { cause?: unknown }).cause = opts.cause;\n }\n }\n}\n\nexport class MantyxAuthError extends MantyxError {\n constructor(message = \"Invalid or missing API key\") {\n super(message, { code: \"unauthorized\", status: 401 });\n this.name = \"MantyxAuthError\";\n }\n}\n\nexport class MantyxToolError extends MantyxError {\n readonly toolName: string;\n\n constructor(toolName: string, message: string) {\n super(`Local tool ${JSON.stringify(toolName)} failed: ${message}`, {\n code: \"local_tool_failed\",\n });\n this.name = \"MantyxToolError\";\n this.toolName = toolName;\n }\n}\n\nexport class MantyxRunError extends MantyxError {\n readonly runId: string;\n readonly subtype: string;\n\n constructor(runId: string, subtype: string, message: string) {\n super(message, { code: subtype });\n this.name = \"MantyxRunError\";\n this.runId = runId;\n this.subtype = subtype;\n }\n}\n","/**\n * Minimal Server-Sent Events parser.\n *\n * Reads a `ReadableStream<Uint8Array>` (the body of a `fetch()` response) and\n * yields parsed events with `id`, `event` and `data` fields. We deliberately\n * keep this dependency-free instead of pulling in `eventsource` / `eventsource-parser`\n * so the SDK has the smallest possible install footprint.\n *\n * Reconnect/replay is handled at a higher layer using `Last-Event-ID` (the\n * default for browsers' `EventSource`) plus a `?lastSeq=` query param so curl\n * users and SSE-via-fetch consumers both work.\n */\n\nexport interface SseEvent {\n id?: string;\n event?: string;\n data: string;\n}\n\nexport interface SseStreamOptions {\n /** AbortSignal for cancellation. */\n signal?: AbortSignal;\n}\n\n/**\n * Async generator yielding parsed SSE events from a fetch response body.\n * Comment frames (`:keep-alive`) are dropped.\n */\nexport async function* readSseStream(\n body: ReadableStream<Uint8Array> | null,\n opts: SseStreamOptions = {},\n): AsyncGenerator<SseEvent, void, void> {\n if (!body) return;\n const reader = body.getReader();\n const decoder = new TextDecoder(\"utf-8\");\n let buffer = \"\";\n\n let cancelled = false;\n const onAbort = (): void => {\n cancelled = true;\n try {\n void reader.cancel();\n } catch {\n // ignore\n }\n };\n if (opts.signal) {\n if (opts.signal.aborted) {\n onAbort();\n } else {\n opts.signal.addEventListener(\"abort\", onAbort, { once: true });\n }\n }\n\n try {\n while (!cancelled) {\n const { done, value } = await reader.read();\n if (done) break;\n buffer += decoder.decode(value, { stream: true });\n // SSE events are separated by a blank line (\\n\\n). Some servers emit \\r\\n.\n let sepIdx: number;\n while ((sepIdx = findSeparator(buffer)) !== -1) {\n const raw = buffer.slice(0, sepIdx);\n buffer = buffer.slice(sepIdx + (buffer.startsWith(\"\\r\", sepIdx) ? 4 : 2));\n const ev = parseEventBlock(raw);\n if (ev) yield ev;\n }\n }\n } finally {\n if (opts.signal) opts.signal.removeEventListener(\"abort\", onAbort);\n try {\n reader.releaseLock();\n } catch {\n // ignore\n }\n }\n}\n\nfunction findSeparator(s: string): number {\n const lf = s.indexOf(\"\\n\\n\");\n const crlf = s.indexOf(\"\\r\\n\\r\\n\");\n if (lf === -1) return crlf;\n if (crlf === -1) return lf;\n return Math.min(lf, crlf);\n}\n\nfunction parseEventBlock(block: string): SseEvent | null {\n const lines = block.split(/\\r?\\n/);\n let id: string | undefined;\n let event: string | undefined;\n const dataLines: string[] = [];\n for (const line of lines) {\n if (line.length === 0) continue;\n if (line.startsWith(\":\")) continue; // comment / heartbeat\n const colonIdx = line.indexOf(\":\");\n const field = colonIdx === -1 ? line : line.slice(0, colonIdx);\n let value = colonIdx === -1 ? \"\" : line.slice(colonIdx + 1);\n if (value.startsWith(\" \")) value = value.slice(1);\n if (field === \"id\") id = value;\n else if (field === \"event\") event = value;\n else if (field === \"data\") dataLines.push(value);\n }\n if (dataLines.length === 0 && id === undefined && event === undefined) {\n return null;\n }\n return {\n ...(id !== undefined ? { id } : {}),\n ...(event !== undefined ? { event } : {}),\n data: dataLines.join(\"\\n\"),\n };\n}\n","/**\n * Public tool helpers for the MANTYX SDK.\n *\n * defineLocalTool({ name, description, parameters, execute })\n * → A tool that runs in the developer's process. The MANTYX server pauses\n * the agent loop, emits a `local_tool_call` event, and waits for the SDK\n * to POST the result back.\n *\n * mantyxTool(id) → Reference an existing workspace `Tool` row by id.\n * mantyxPluginTool(name) → Reference a built-in plugin tool by `@plugin/tool` name.\n */\nimport type { z } from \"zod\";\n\nexport type ZodLikeObject = z.ZodType<Record<string, unknown>> & {\n _def?: unknown;\n parse?: (value: unknown) => unknown;\n};\n\nexport interface LocalTool<TArgs = Record<string, unknown>> {\n readonly kind: \"local\";\n readonly name: string;\n readonly description: string;\n readonly parameters: ZodLikeObject | undefined;\n readonly execute: (args: TArgs) => Promise<string> | string;\n}\n\nexport interface MantyxToolRef {\n readonly kind: \"mantyx\";\n readonly id: string;\n}\n\nexport interface MantyxPluginToolRef {\n readonly kind: \"mantyx_plugin\";\n readonly name: string;\n}\n\nexport type ToolRef = MantyxToolRef | MantyxPluginToolRef | LocalTool;\n\nexport interface DefineLocalToolOptions<T extends ZodLikeObject | undefined> {\n /** Lowercase alphanumeric + underscore, max 64 chars. */\n name: string;\n description?: string;\n parameters?: T;\n execute: (args: T extends ZodLikeObject ? z.infer<T> : Record<string, unknown>) => Promise<string> | string;\n}\n\nexport function defineLocalTool<T extends ZodLikeObject | undefined>(\n opts: DefineLocalToolOptions<T>,\n): LocalTool {\n if (!/^[a-zA-Z0-9_]{1,64}$/.test(opts.name)) {\n throw new Error(\n `Invalid local tool name ${JSON.stringify(opts.name)}: must match /^[a-zA-Z0-9_]{1,64}$/`,\n );\n }\n return {\n kind: \"local\",\n name: opts.name,\n description: opts.description ?? \"\",\n parameters: opts.parameters,\n execute: opts.execute as LocalTool[\"execute\"],\n };\n}\n\nexport function mantyxTool(id: string): MantyxToolRef {\n if (typeof id !== \"string\" || id.length === 0) {\n throw new Error(\"mantyxTool(id): id must be a non-empty string\");\n }\n return { kind: \"mantyx\", id };\n}\n\nexport function mantyxPluginTool(name: string): MantyxPluginToolRef {\n if (typeof name !== \"string\" || !name.startsWith(\"@\") || !name.includes(\"/\")) {\n throw new Error(\n `mantyxPluginTool(name): expected \"@plugin-slug/tool-name\", got ${JSON.stringify(name)}`,\n );\n }\n return { kind: \"mantyx_plugin\", name };\n}\n\nexport function isLocalTool(t: ToolRef): t is LocalTool {\n return t.kind === \"local\";\n}\n","/**\n * Lightweight Zod → JSON Schema converter for tool parameter definitions.\n *\n * Tries `z.toJSONSchema` (Zod v4+) first; falls back to a hand-rolled walker\n * for v3 schemas so the SDK works on a wide range of zod versions.\n *\n * The output is a JSON-Schema-shaped object with `type: \"object\"`, `properties`,\n * and `required`. The MANTYX server feeds this to LLM providers verbatim, so\n * unsupported zod features (effects, transforms, intersections) degrade to a\n * permissive `\"object\"` description rather than failing.\n */\nimport { z } from \"zod\";\n\ntype JsonSchema = Record<string, unknown>;\n\ninterface ZodLikeWithToJsonSchema {\n toJSONSchema?: (schema: unknown) => JsonSchema;\n}\n\nexport function zodToJsonSchema(schema: z.ZodType<unknown>): JsonSchema {\n const builtIn = (z as unknown as ZodLikeWithToJsonSchema).toJSONSchema;\n if (typeof builtIn === \"function\") {\n try {\n const out = builtIn.call(z, schema) as JsonSchema;\n if (out && typeof out === \"object\") return out;\n } catch {\n // fall through to manual converter\n }\n }\n return convertNode(schema);\n}\n\nfunction convertNode(schema: z.ZodType<unknown>): JsonSchema {\n const def = (schema as unknown as { _def?: { typeName?: string } })._def;\n const typeName = def?.typeName;\n switch (typeName) {\n case \"ZodString\":\n return { type: \"string\" };\n case \"ZodNumber\":\n return { type: \"number\" };\n case \"ZodBoolean\":\n return { type: \"boolean\" };\n case \"ZodNull\":\n return { type: \"null\" };\n case \"ZodLiteral\": {\n const value = (def as { value?: unknown }).value;\n return { const: value, type: typeof value };\n }\n case \"ZodEnum\": {\n const values = (def as { values?: readonly string[] }).values ?? [];\n return { type: \"string\", enum: [...values] };\n }\n case \"ZodArray\": {\n const inner = (def as { type?: z.ZodType<unknown> }).type;\n return {\n type: \"array\",\n items: inner ? convertNode(inner) : {},\n };\n }\n case \"ZodOptional\":\n case \"ZodNullable\": {\n const inner = (def as { innerType?: z.ZodType<unknown> }).innerType;\n return inner ? convertNode(inner) : {};\n }\n case \"ZodDefault\": {\n const inner = (def as { innerType?: z.ZodType<unknown> }).innerType;\n return inner ? convertNode(inner) : {};\n }\n case \"ZodObject\": {\n const shape = (def as { shape?: () => Record<string, z.ZodType<unknown>> }).shape;\n const fields = typeof shape === \"function\" ? shape() : (shape as Record<string, z.ZodType<unknown>> | undefined);\n const properties: Record<string, JsonSchema> = {};\n const required: string[] = [];\n if (fields) {\n for (const [key, value] of Object.entries(fields)) {\n properties[key] = convertNode(value);\n const innerDef = (value as unknown as { _def?: { typeName?: string } })._def;\n const innerTypeName = innerDef?.typeName;\n if (innerTypeName !== \"ZodOptional\" && innerTypeName !== \"ZodDefault\") {\n required.push(key);\n }\n }\n }\n const out: JsonSchema = { type: \"object\", properties };\n if (required.length > 0) out.required = required;\n return out;\n }\n default:\n return {};\n }\n}\n\n/**\n * Coerce a JSON-Schema-shaped value into a wire object suitable for the\n * MANTYX local-tool definition payload. Accepts either a Zod schema or an\n * already-shaped JSON Schema object.\n */\nexport function toToolParametersWire(\n parameters: z.ZodType<unknown> | JsonSchema | undefined,\n): JsonSchema {\n if (!parameters) return { type: \"object\", properties: {} };\n if (typeof (parameters as { _def?: unknown })._def !== \"undefined\") {\n return zodToJsonSchema(parameters as z.ZodType<unknown>);\n }\n return parameters as JsonSchema;\n}\n","/**\n * MANTYX SDK client: HTTP plumbing, model catalog, run + session drivers.\n */\nimport {\n MantyxAuthError,\n MantyxError,\n MantyxNetworkError,\n MantyxRunError,\n MantyxToolError,\n} from \"./errors.js\";\nimport { readSseStream } from \"./sse.js\";\nimport type { LocalTool, ToolRef } from \"./tools.js\";\nimport { isLocalTool } from \"./tools.js\";\nimport { toToolParametersWire } from \"./zod-to-json-schema.js\";\n\nexport const DEFAULT_BASE_URL = \"https://api.mantyx.com\";\n\nexport interface MantyxClientOptions {\n apiKey: string;\n workspaceSlug: string;\n /** Defaults to `https://api.mantyx.com`. Override for self-hosted instances. */\n baseUrl?: string;\n /** Optional `fetch` override (e.g. node-fetch wrapper, or a custom HTTP client). */\n fetch?: typeof fetch;\n /** Default per-request timeout in milliseconds. Default: 60s. */\n timeoutMs?: number;\n}\n\nexport interface ModelInfo {\n id: string;\n label: string;\n provider: string;\n vendorModelId: string;\n source: \"workspace_provider\" | \"platform_offering\";\n contextWindowTokens: number | null;\n pricing: {\n inputPer1MUsd: number | null;\n outputPer1MUsd: number | null;\n cacheReadPer1MUsd: number | null;\n } | null;\n}\n\nexport interface ModelCatalog {\n models: ModelInfo[];\n defaultModelId: string | null;\n}\n\nexport interface AgentSpecBase {\n name?: string;\n /**\n * Reference to a persisted MANTYX agent in this workspace. When set, the\n * server hydrates `systemPrompt`, `modelId`, and the agent's own tools\n * (memory, skills, plugin tools, …) from the Agent row at run time, and any\n * `tools` you supply here are merged on top — typically `local` tools the\n * SDK wants the agent to be able to call back into.\n *\n * Either `agentId` or `systemPrompt` must be set.\n */\n agentId?: string;\n /** Required unless `agentId` is set. */\n systemPrompt?: string;\n modelId?: string;\n tools?: ToolRef[];\n budgets?: { maxToolTurns?: number };\n /**\n * Flat string→string KV carried alongside the run / session for\n * observability. Use it to tag runs with your own application identifiers\n * (customer id, environment, workflow name, …) — the values are visible in\n * the MANTYX dashboard and can be filtered there.\n *\n * Limits enforced server-side: max 16 entries; keys match\n * `[A-Za-z0-9._-]{1,64}`; values are strings ≤ 256 chars; serialized JSON\n * ≤ 4 KB. For session-scoped runs, the session's metadata is inherited and\n * any per-message override is merged on top.\n */\n metadata?: Record<string, string>;\n}\n\nexport interface RunSpec extends AgentSpecBase {\n prompt?: string;\n messages?: Array<{ role: \"user\" | \"assistant\" | \"system\"; content: string }>;\n /** Receives streaming assistant text deltas. */\n onAssistantDelta?: (delta: string) => void;\n /** Receives raw events (assistant_message, local_tool_call, tool_result, ...) for advanced consumers. */\n onEvent?: (event: RunEvent) => void;\n /** Aborts the run on the client and best-effort cancels server-side. */\n signal?: AbortSignal;\n}\n\nexport type SessionSpec = AgentSpecBase;\n\nexport interface RunResult {\n runId: string;\n text: string;\n events: RunEvent[];\n}\n\nexport interface RunEventBase {\n seq: number;\n type: string;\n}\n\nexport interface AssistantDeltaEvent extends RunEventBase {\n type: \"assistant_delta\";\n text: string;\n}\n\nexport interface ThinkingDeltaEvent extends RunEventBase {\n type: \"thinking_delta\";\n text: string;\n}\n\nexport interface AssistantMessageEvent extends RunEventBase {\n type: \"assistant_message\";\n text: string;\n}\n\nexport interface ServerToolResultEvent extends RunEventBase {\n type: \"tool_result\";\n name: string;\n args?: Record<string, unknown>;\n ok?: boolean;\n summary?: string;\n phase?: \"start\" | \"end\";\n}\n\nexport interface LocalToolCallEvent extends RunEventBase {\n type: \"local_tool_call\";\n toolUseId: string;\n name: string;\n args: Record<string, unknown>;\n}\n\nexport interface LocalToolResultInEvent extends RunEventBase {\n type: \"local_tool_result_in\";\n toolUseId: string;\n result?: string;\n error?: string;\n}\n\nexport interface ResultEvent extends RunEventBase {\n type: \"result\";\n subtype: string;\n text?: string;\n error?: string;\n}\n\nexport interface ErrorEvent extends RunEventBase {\n type: \"error\";\n error: string;\n code?: string;\n}\n\nexport interface CancelledEvent extends RunEventBase {\n type: \"cancelled\";\n reason?: string;\n}\n\nexport type RunEvent =\n | AssistantDeltaEvent\n | ThinkingDeltaEvent\n | AssistantMessageEvent\n | ServerToolResultEvent\n | LocalToolCallEvent\n | LocalToolResultInEvent\n | ResultEvent\n | ErrorEvent\n | CancelledEvent\n | (RunEventBase & { type: string; [key: string]: unknown });\n\nexport interface SessionInfo {\n id: string;\n name: string;\n status: \"active\" | \"ended\";\n createdAt: string;\n lastUsedAt: string;\n endedAt: string | null;\n agentSpec: AgentSpecBase;\n messages: Array<{ role: \"user\" | \"assistant\" | \"system\"; content: string }>;\n /** Metadata that was attached to the session at create time, returned for observability. */\n metadata: Record<string, string>;\n}\n\nexport class MantyxClient {\n readonly options: Required<Pick<MantyxClientOptions, \"apiKey\" | \"workspaceSlug\" | \"baseUrl\">> & {\n fetch: typeof fetch;\n timeoutMs: number;\n };\n\n constructor(opts: MantyxClientOptions) {\n if (!opts.apiKey || typeof opts.apiKey !== \"string\") {\n throw new MantyxError(\"apiKey is required\");\n }\n if (!opts.workspaceSlug || typeof opts.workspaceSlug !== \"string\") {\n throw new MantyxError(\"workspaceSlug is required\");\n }\n const f = opts.fetch ?? globalThis.fetch;\n if (typeof f !== \"function\") {\n throw new MantyxError(\n \"Global fetch is not available; pass a custom `fetch` implementation in MantyxClientOptions.\",\n );\n }\n this.options = {\n apiKey: opts.apiKey,\n workspaceSlug: opts.workspaceSlug,\n baseUrl: (opts.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, \"\"),\n fetch: f,\n timeoutMs: opts.timeoutMs ?? 60_000,\n };\n }\n\n // -------------------------------------------------------------- Models\n\n async listModels(): Promise<ModelCatalog> {\n return this.request<ModelCatalog>({\n method: \"GET\",\n path: \"/models\",\n });\n }\n\n // ------------------------------------------------------------- One-shot\n\n async runAgent(spec: RunSpec): Promise<RunResult> {\n const handlers = collectLocalHandlers(spec.tools ?? []);\n const created = await this.request<{ runId: string; streamUrl: string }>({\n method: \"POST\",\n path: \"/agent-runs\",\n body: serializeAgentSpec(spec, {\n prompt: spec.prompt,\n messages: spec.messages,\n }),\n });\n return this.driveRun(created.runId, handlers, {\n ...(spec.onAssistantDelta ? { onAssistantDelta: spec.onAssistantDelta } : {}),\n ...(spec.onEvent ? { onEvent: spec.onEvent } : {}),\n ...(spec.signal ? { signal: spec.signal } : {}),\n });\n }\n\n async *streamAgent(spec: RunSpec): AsyncGenerator<RunEvent, void, void> {\n const handlers = collectLocalHandlers(spec.tools ?? []);\n const created = await this.request<{ runId: string; streamUrl: string }>({\n method: \"POST\",\n path: \"/agent-runs\",\n body: serializeAgentSpec(spec, {\n prompt: spec.prompt,\n messages: spec.messages,\n }),\n });\n yield* this.streamRunEvents(created.runId, handlers, spec.signal);\n }\n\n // ------------------------------------------------------------- Sessions\n\n async createSession(spec: SessionSpec): Promise<AgentSession> {\n const handlers = collectLocalHandlers(spec.tools ?? []);\n const created = await this.request<{ sessionId: string; name: string; createdAt: string }>({\n method: \"POST\",\n path: \"/agent-sessions\",\n body: serializeAgentSpec(spec),\n });\n return new AgentSession(this, created.sessionId, handlers);\n }\n\n async resumeSession(\n sessionId: string,\n opts: { tools?: ToolRef[] } = {},\n ): Promise<AgentSession> {\n // Verify the session exists and is still active. Optionally refresh tool defs.\n await this.getSessionInfo(sessionId);\n const handlers = collectLocalHandlers(opts.tools ?? []);\n return new AgentSession(this, sessionId, handlers, opts.tools);\n }\n\n async endSession(sessionId: string): Promise<void> {\n await this.request<{ ok: boolean }>({\n method: \"DELETE\",\n path: `/agent-sessions/${encodeURIComponent(sessionId)}`,\n });\n }\n\n async getSessionInfo(sessionId: string): Promise<SessionInfo> {\n return this.request<SessionInfo>({\n method: \"GET\",\n path: `/agent-sessions/${encodeURIComponent(sessionId)}`,\n });\n }\n\n // ----------------------------------------------------------- Internals\n\n /** Drive an existing run to completion (collect events, dispatch local tools). */\n async driveRun(\n runId: string,\n handlers: Map<string, LocalTool>,\n opts: {\n onAssistantDelta?: (delta: string) => void;\n onEvent?: (event: RunEvent) => void;\n signal?: AbortSignal;\n } = {},\n ): Promise<RunResult> {\n const collected: RunEvent[] = [];\n let finalText = \"\";\n for await (const ev of this.streamRunEvents(runId, handlers, opts.signal)) {\n collected.push(ev);\n if (opts.onEvent) opts.onEvent(ev);\n if (ev.type === \"assistant_delta\" && opts.onAssistantDelta) {\n opts.onAssistantDelta((ev as AssistantDeltaEvent).text);\n }\n if (ev.type === \"result\") {\n const r = ev as ResultEvent;\n if (r.subtype === \"success\") {\n finalText = typeof r.text === \"string\" ? r.text : \"\";\n } else {\n throw new MantyxRunError(runId, r.subtype, r.error ?? r.subtype);\n }\n } else if (ev.type === \"error\") {\n const e = ev as ErrorEvent;\n throw new MantyxRunError(runId, e.code ?? \"error\", e.error);\n } else if (ev.type === \"cancelled\") {\n throw new MantyxRunError(runId, \"cancelled\", \"Run was cancelled\");\n }\n }\n return { runId, text: finalText, events: collected };\n }\n\n async *streamRunEvents(\n runId: string,\n handlers: Map<string, LocalTool>,\n signal?: AbortSignal,\n ): AsyncGenerator<RunEvent, void, void> {\n const url = this.absoluteUrl(`/agent-runs/${encodeURIComponent(runId)}/stream`);\n let lastSeq = 0;\n while (true) {\n const reqUrl = lastSeq > 0 ? `${url}?lastSeq=${lastSeq}` : url;\n const res = await this.options.fetch(reqUrl, {\n method: \"GET\",\n headers: {\n ...this.authHeaders(),\n Accept: \"text/event-stream\",\n ...(lastSeq > 0 ? { \"Last-Event-ID\": String(lastSeq) } : {}),\n },\n ...(signal ? { signal } : {}),\n }).catch((err: unknown) => {\n throw new MantyxNetworkError(`Failed to open SSE stream: ${(err as Error).message}`, {\n cause: err,\n });\n });\n if (!res.ok) {\n throw await this.errorFromResponse(res);\n }\n let terminal = false;\n try {\n for await (const sseEvent of readSseStream(res.body, { ...(signal ? { signal } : {}) })) {\n let data: Record<string, unknown> = {};\n try {\n data = JSON.parse(sseEvent.data || \"{}\") as Record<string, unknown>;\n } catch {\n data = {};\n }\n const evType = sseEvent.event ?? (data.type as string | undefined) ?? \"message\";\n const seq = typeof data.seq === \"number\" ? data.seq : lastSeq;\n if (typeof seq === \"number\" && seq > lastSeq) lastSeq = seq;\n const ev = { seq, type: evType, ...data } as RunEvent;\n yield ev;\n if (evType === \"local_tool_call\") {\n const localEv = ev as LocalToolCallEvent;\n void this.dispatchLocalTool(runId, localEv, handlers).catch((err) => {\n // best-effort logging; the run will surface a `result/error` if the\n // server eventually times out.\n console.error(\"[mantyx-sdk] local tool dispatch failed:\", err);\n });\n }\n if (evType === \"result\" || evType === \"error\" || evType === \"cancelled\") {\n terminal = true;\n return;\n }\n }\n } catch (err) {\n if (signal?.aborted) {\n throw new MantyxRunError(runId, \"cancelled\", \"Run was cancelled by the client\");\n }\n // Network blip — retry after a tiny backoff with `?lastSeq=`.\n await sleep(500);\n continue;\n }\n if (terminal) return;\n // Stream closed without a terminal event (server restart, etc.) — reconnect.\n }\n }\n\n async dispatchLocalTool(\n runId: string,\n ev: LocalToolCallEvent,\n handlers: Map<string, LocalTool>,\n ): Promise<void> {\n const handler = handlers.get(ev.name);\n if (!handler) {\n await this.postToolResult(runId, ev.toolUseId, {\n error: `No local handler registered for tool ${JSON.stringify(ev.name)}`,\n });\n return;\n }\n try {\n const args = handler.parameters ? handler.parameters.parse?.(ev.args) ?? ev.args : ev.args;\n const out = await handler.execute(args as Record<string, unknown>);\n const resultText = typeof out === \"string\" ? out : JSON.stringify(out);\n await this.postToolResult(runId, ev.toolUseId, { result: resultText });\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n await this.postToolResult(runId, ev.toolUseId, {\n error: new MantyxToolError(handler.name, message).message,\n });\n }\n }\n\n async postToolResult(\n runId: string,\n toolUseId: string,\n payload: { result?: string; error?: string },\n ): Promise<void> {\n await this.request<{ ok: boolean }>({\n method: \"POST\",\n path: `/agent-runs/${encodeURIComponent(runId)}/tool-results`,\n body: { toolUseId, ...payload },\n });\n }\n\n async cancelRun(runId: string): Promise<void> {\n await this.request<{ ok: boolean }>({\n method: \"POST\",\n path: `/agent-runs/${encodeURIComponent(runId)}/cancel`,\n });\n }\n\n // -------------------------------------------------------------- HTTP\n\n private absoluteUrl(path: string): string {\n return `${this.options.baseUrl}/api/v1/workspaces/${encodeURIComponent(this.options.workspaceSlug)}${path}`;\n }\n\n private authHeaders(): Record<string, string> {\n return { Authorization: `Bearer ${this.options.apiKey}` };\n }\n\n async request<T>(args: {\n method: string;\n path: string;\n body?: unknown;\n timeoutMs?: number;\n }): Promise<T> {\n const url = this.absoluteUrl(args.path);\n const ctrl = new AbortController();\n const t = setTimeout(() => ctrl.abort(), args.timeoutMs ?? this.options.timeoutMs);\n try {\n const res = await this.options.fetch(url, {\n method: args.method,\n headers: {\n ...this.authHeaders(),\n ...(args.body !== undefined ? { \"Content-Type\": \"application/json\" } : {}),\n Accept: \"application/json\",\n },\n ...(args.body !== undefined ? { body: JSON.stringify(args.body) } : {}),\n signal: ctrl.signal,\n }).catch((err: unknown) => {\n if (ctrl.signal.aborted) {\n throw new MantyxNetworkError(`Request timed out after ${args.timeoutMs ?? this.options.timeoutMs}ms`);\n }\n throw new MantyxNetworkError(`Network error: ${(err as Error).message}`, { cause: err });\n });\n if (!res.ok) {\n throw await this.errorFromResponse(res);\n }\n const text = await res.text();\n if (!text) return undefined as unknown as T;\n try {\n return JSON.parse(text) as T;\n } catch (err) {\n throw new MantyxError(`Failed to parse JSON response: ${(err as Error).message}`);\n }\n } finally {\n clearTimeout(t);\n }\n }\n\n private async errorFromResponse(res: Response): Promise<MantyxError> {\n let body: { error?: string; code?: string; hint?: string } = {};\n try {\n body = (await res.json()) as typeof body;\n } catch {\n // ignore\n }\n if (res.status === 401) {\n return new MantyxAuthError(body.error ?? \"Invalid API key\");\n }\n return new MantyxError(body.error ?? `HTTP ${res.status}`, {\n code: body.code ?? `http_${res.status}`,\n status: res.status,\n ...(body.hint ? { hint: body.hint } : {}),\n });\n }\n}\n\n// ---------------------------------------------------------------- Sessions\n\nexport class AgentSession {\n readonly id: string;\n readonly client: MantyxClient;\n private readonly handlers: Map<string, LocalTool>;\n private readonly toolsForResume: ToolRef[] | undefined;\n\n constructor(\n client: MantyxClient,\n id: string,\n handlers: Map<string, LocalTool>,\n toolsForResume?: ToolRef[],\n ) {\n this.client = client;\n this.id = id;\n this.handlers = handlers;\n this.toolsForResume = toolsForResume;\n }\n\n async send(\n prompt: string,\n opts: {\n onAssistantDelta?: (s: string) => void;\n signal?: AbortSignal;\n /**\n * Per-message metadata override. Server-side this is merged on top of\n * the session's metadata at run-creation time (run-level keys win).\n * Useful for tagging individual turns (e.g. `{ \"trace_id\": \"abc\" }`).\n */\n metadata?: Record<string, string>;\n } = {},\n ): Promise<RunResult> {\n const created = await this.client.request<{ runId: string; streamUrl: string }>({\n method: \"POST\",\n path: `/agent-sessions/${encodeURIComponent(this.id)}/messages`,\n body: {\n prompt,\n ...(this.toolsForResume ? { tools: serializeToolRefs(this.toolsForResume) } : {}),\n ...(opts.metadata && Object.keys(opts.metadata).length > 0\n ? { metadata: opts.metadata }\n : {}),\n },\n });\n return this.client.driveRun(created.runId, this.handlers, {\n ...(opts.onAssistantDelta ? { onAssistantDelta: opts.onAssistantDelta } : {}),\n ...(opts.signal ? { signal: opts.signal } : {}),\n });\n }\n\n async *stream(\n prompt: string,\n opts: { signal?: AbortSignal; metadata?: Record<string, string> } = {},\n ): AsyncGenerator<RunEvent, void, void> {\n const created = await this.client.request<{ runId: string; streamUrl: string }>({\n method: \"POST\",\n path: `/agent-sessions/${encodeURIComponent(this.id)}/messages`,\n body: {\n prompt,\n ...(this.toolsForResume ? { tools: serializeToolRefs(this.toolsForResume) } : {}),\n ...(opts.metadata && Object.keys(opts.metadata).length > 0\n ? { metadata: opts.metadata }\n : {}),\n },\n });\n yield* this.client.streamRunEvents(created.runId, this.handlers, opts.signal);\n }\n\n async history(): Promise<Array<{ role: \"user\" | \"assistant\" | \"system\"; content: string }>> {\n const info = await this.client.getSessionInfo(this.id);\n return info.messages;\n }\n\n async info(): Promise<SessionInfo> {\n return this.client.getSessionInfo(this.id);\n }\n\n async end(): Promise<void> {\n await this.client.endSession(this.id);\n }\n}\n\n// ---------------------------------------------------------------- Helpers\n\nfunction serializeAgentSpec(\n spec: AgentSpecBase,\n extra: { prompt?: string; messages?: Array<{ role: string; content: string }> } = {},\n): Record<string, unknown> {\n if (!spec.agentId && (typeof spec.systemPrompt !== \"string\" || spec.systemPrompt.length === 0)) {\n throw new MantyxError(\"Either `agentId` or `systemPrompt` is required\");\n }\n const body: Record<string, unknown> = {\n tools: serializeToolRefs(spec.tools ?? []),\n };\n if (typeof spec.systemPrompt === \"string\") body.systemPrompt = spec.systemPrompt;\n if (spec.agentId) body.agentId = spec.agentId;\n if (spec.name) body.name = spec.name;\n if (spec.modelId) body.modelId = spec.modelId;\n if (spec.budgets) body.budgets = spec.budgets;\n if (spec.metadata && Object.keys(spec.metadata).length > 0) body.metadata = spec.metadata;\n if (extra.prompt !== undefined) body.prompt = extra.prompt;\n if (extra.messages !== undefined) body.messages = extra.messages;\n return body;\n}\n\nfunction serializeToolRefs(tools: ToolRef[]): unknown[] {\n return tools.map((t) => {\n if (t.kind === \"mantyx\") return { kind: \"mantyx\", id: t.id };\n if (t.kind === \"mantyx_plugin\") return { kind: \"mantyx_plugin\", name: t.name };\n return {\n kind: \"local\",\n name: t.name,\n description: t.description,\n parameters: toToolParametersWire(t.parameters),\n };\n });\n}\n\nfunction collectLocalHandlers(tools: ToolRef[]): Map<string, LocalTool> {\n const map = new Map<string, LocalTool>();\n for (const t of tools) {\n if (isLocalTool(t)) map.set(t.name, t);\n }\n return map;\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((r) => setTimeout(r, ms));\n}\n","/**\n * Release version — synced from repo root VERSION (`npm run sync-version`).\n */\nexport const SDK_VERSION = \"0.1.0\";\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
# Agent Runs — wire protocol
|
|
2
|
+
|
|
3
|
+
This document specifies the public wire protocol that the MANTYX agent-runs API
|
|
4
|
+
speaks with SDKs. It is the source of truth for anyone implementing a new
|
|
5
|
+
client (Python, Rust, Java…) and is shipped with each first-party SDK so the
|
|
6
|
+
SDK repository can stand on its own when it is extracted from this monorepo.
|
|
7
|
+
|
|
8
|
+
The companion document for this protocol — server-side overview, internals,
|
|
9
|
+
deployment notes — is [`docs/agent-runs.md`](./agent-runs.md).
|
|
10
|
+
|
|
11
|
+
## 1. Concepts
|
|
12
|
+
|
|
13
|
+
**Ephemeral agent.** A run-time agent that is *defined by the request* rather
|
|
14
|
+
than persisted as a row in MANTYX's `Agent` table. The full spec (system
|
|
15
|
+
prompt, model, tools) is stored as part of each session/run for observability
|
|
16
|
+
but is not editable from the dashboard.
|
|
17
|
+
|
|
18
|
+
**Tool refs.** Three flavours, all carried inside the agent spec:
|
|
19
|
+
|
|
20
|
+
| `kind` | Resolved by | Notes |
|
|
21
|
+
| ---------------- | ----------- | ----- |
|
|
22
|
+
| `mantyx` | server | A workspace `Tool` row referenced by id (HTTP / Code / Plugin). |
|
|
23
|
+
| `mantyx_plugin` | server | A platform plugin tool referenced by name. |
|
|
24
|
+
| `local` | client | Defined and executed in the SDK's process. |
|
|
25
|
+
|
|
26
|
+
When the model calls a `local` tool, MANTYX pauses the agent loop, emits a
|
|
27
|
+
`local_tool_call` event over SSE and waits for the SDK to POST a tool-result
|
|
28
|
+
back via HTTP.
|
|
29
|
+
|
|
30
|
+
**One-shot run vs. session.** A run is an LLM execution. Runs may be:
|
|
31
|
+
|
|
32
|
+
- *one-shot* (`POST /agent-runs`) — fire-and-stream, no persistent state apart
|
|
33
|
+
from observability.
|
|
34
|
+
- *session-scoped* (`POST /agent-sessions/:id/messages`) — the run inherits the
|
|
35
|
+
session's full message history, and the new user/assistant turns are
|
|
36
|
+
appended back to the session on success.
|
|
37
|
+
|
|
38
|
+
## 2. Authentication
|
|
39
|
+
|
|
40
|
+
All SDK-facing endpoints sit under
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
/api/v1/workspaces/{workspaceSlug}/...
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
and are authenticated with a workspace API key with usage `developer_api`:
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
Authorization: Bearer <api-key>
|
|
50
|
+
# or, equivalently:
|
|
51
|
+
X-API-Key: <api-key>
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
The workspace slug in the URL must match the key's tenant. Mismatches return
|
|
55
|
+
`404 not_found`. Missing/invalid keys return `401 unauthorized`. Rate limits
|
|
56
|
+
follow the workspace's existing developer-API sliding-window policy.
|
|
57
|
+
|
|
58
|
+
## 3. Models
|
|
59
|
+
|
|
60
|
+
```
|
|
61
|
+
GET /api/v1/workspaces/{workspaceSlug}/models
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Returns models the API key's workspace can run, including BYOK providers and
|
|
65
|
+
platform-hosted offerings visible to the workspace's tier.
|
|
66
|
+
|
|
67
|
+
```jsonc
|
|
68
|
+
{
|
|
69
|
+
"models": [
|
|
70
|
+
{
|
|
71
|
+
"id": "platform:cm6abc123",
|
|
72
|
+
"label": "Anthropic Claude Sonnet 4.5 (platform)",
|
|
73
|
+
"provider": "anthropic",
|
|
74
|
+
"vendorModelId": "claude-sonnet-4-5",
|
|
75
|
+
"source": "platform_offering",
|
|
76
|
+
"contextWindowTokens": 200000,
|
|
77
|
+
"pricing": { "inputPer1MUsd": 3.0, "outputPer1MUsd": 15.0, "cacheReadPer1MUsd": 0.3 }
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
"id": "provider:cm6def456",
|
|
81
|
+
"label": "OpenAI (workspace BYOK) — gpt-5.5",
|
|
82
|
+
"provider": "openai",
|
|
83
|
+
"vendorModelId": "gpt-5.5",
|
|
84
|
+
"source": "workspace_provider",
|
|
85
|
+
"contextWindowTokens": 200000,
|
|
86
|
+
"pricing": null
|
|
87
|
+
}
|
|
88
|
+
],
|
|
89
|
+
"defaultModelId": "platform:cm6abc123"
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
The `id` is the canonical value the SDK passes back as `RunSpec.modelId` /
|
|
94
|
+
`SessionSpec.modelId`. The server accepts three additional shorthand forms:
|
|
95
|
+
|
|
96
|
+
- `provider:<id>:<vendor>` — pin a specific BYOK provider but override the
|
|
97
|
+
vendor model id.
|
|
98
|
+
- `<vendorModelId>` — bare vendor id; only succeeds if exactly one workspace
|
|
99
|
+
provider can run it.
|
|
100
|
+
- `undefined`/omitted — falls back to the workspace default provider's
|
|
101
|
+
default model.
|
|
102
|
+
|
|
103
|
+
Invalid `modelId` values return `400 invalid_model` with a candidate list
|
|
104
|
+
in the body when applicable.
|
|
105
|
+
|
|
106
|
+
## 4. Agent spec
|
|
107
|
+
|
|
108
|
+
The agent spec is the body shape used by `POST /agent-runs` and `POST
|
|
109
|
+
/agent-sessions`:
|
|
110
|
+
|
|
111
|
+
```jsonc
|
|
112
|
+
{
|
|
113
|
+
"name": "ephemeral", // optional, observability only
|
|
114
|
+
"agentId": "agent_cm6abc123", // optional — see §4.1
|
|
115
|
+
"systemPrompt": "You are helpful.", // required unless agentId is set
|
|
116
|
+
"modelId": "platform:cm6abc123", // optional, see §3
|
|
117
|
+
"tools": [
|
|
118
|
+
{ "kind": "mantyx", "id": "tool_cm6..." },
|
|
119
|
+
{ "kind": "mantyx_plugin", "name": "web_search" },
|
|
120
|
+
{
|
|
121
|
+
"kind": "local",
|
|
122
|
+
"name": "read_file",
|
|
123
|
+
"description": "Read a file from the user's machine",
|
|
124
|
+
"parameters": { // JSON Schema for the args object
|
|
125
|
+
"type": "object",
|
|
126
|
+
"properties": { "path": { "type": "string" } },
|
|
127
|
+
"required": ["path"],
|
|
128
|
+
"additionalProperties": false
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
],
|
|
132
|
+
"budgets": { "maxToolTurns": 32 }, // optional safety cap
|
|
133
|
+
"metadata": { // optional, see §4.2
|
|
134
|
+
"customer": "acme",
|
|
135
|
+
"env": "prod"
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
`POST /agent-runs` additionally accepts `prompt` *or* `messages` (an array of
|
|
141
|
+
`{role, content}`). Sending both is a `400 invalid_request`.
|
|
142
|
+
|
|
143
|
+
### 4.1 Triggering a persisted MANTYX agent (`agentId`)
|
|
144
|
+
|
|
145
|
+
Set `agentId` to the `id` of a workspace `Agent` to run that agent instead of
|
|
146
|
+
defining an ephemeral one inline. When `agentId` is set:
|
|
147
|
+
|
|
148
|
+
- `systemPrompt` becomes optional. If omitted, the server uses the agent's
|
|
149
|
+
stored system prompt at run time.
|
|
150
|
+
- `modelId` becomes optional. If omitted, the server uses the agent's
|
|
151
|
+
configured LLM provider (or the workspace automation provider if the agent
|
|
152
|
+
has *Use workspace default model* turned on).
|
|
153
|
+
- The agent's own tools are loaded from its workspace configuration —
|
|
154
|
+
including memory, skills, and plugin tools — and your `tools` array is
|
|
155
|
+
**merged on top**. This is typically used to attach `local` tools so the
|
|
156
|
+
agent can call back into your process for this run, without needing to
|
|
157
|
+
edit the agent's stored tool list.
|
|
158
|
+
- The API key must be authorized for this `agentId`. Keys created with an
|
|
159
|
+
empty `agentIds` allowlist (= "all agents") work for any agent in the
|
|
160
|
+
workspace; otherwise the agent must be in the key's allowlist or the call
|
|
161
|
+
returns `403 forbidden`.
|
|
162
|
+
- An unknown / cross-workspace `agentId` returns `403` (the API key check
|
|
163
|
+
fires first); a malformed `agentId` returns `400`.
|
|
164
|
+
|
|
165
|
+
Both `agentId` and `systemPrompt` may be supplied. The agent's stored prompt
|
|
166
|
+
wins; the inline `systemPrompt` is ignored.
|
|
167
|
+
|
|
168
|
+
### 4.2 `metadata` (developer-supplied KV for filtering)
|
|
169
|
+
|
|
170
|
+
`metadata` is a flat string→string KV that is **persisted alongside the run /
|
|
171
|
+
session** and surfaced in the MANTYX dashboard. Use it to tag runs with your
|
|
172
|
+
own application identifiers (`customer`, `env`, `workflow`, `trace_id`, …) so
|
|
173
|
+
your team can filter the observability UI without reverse-engineering the
|
|
174
|
+
prompt.
|
|
175
|
+
|
|
176
|
+
Validation (server-side, `400 invalid_request` on violation):
|
|
177
|
+
|
|
178
|
+
| Constraint | Limit |
|
|
179
|
+
| ------------------------- | ---------------------------------- |
|
|
180
|
+
| Max entries | 16 |
|
|
181
|
+
| Key pattern | `^[A-Za-z0-9._-]{1,64}$` |
|
|
182
|
+
| Value type / length | string ≤ 256 chars |
|
|
183
|
+
| Serialized JSON size | ≤ 4 KB |
|
|
184
|
+
|
|
185
|
+
For session-scoped runs the inheritance rules are:
|
|
186
|
+
|
|
187
|
+
- `POST /agent-sessions { metadata }` — sets the session's metadata; this is
|
|
188
|
+
inherited by every run created through `POST /agent-sessions/:id/messages`.
|
|
189
|
+
- `POST /agent-sessions/:id/messages { metadata }` — optional per-message
|
|
190
|
+
override. The server snapshots `session.metadata` ⊕ override (run-level
|
|
191
|
+
keys win) onto the run row at creation time. Later edits to the session
|
|
192
|
+
metadata do not retroactively rewrite past runs.
|
|
193
|
+
|
|
194
|
+
Metadata is returned on every read: `GET /agent-runs/:id`,
|
|
195
|
+
`GET /agent-sessions/:id`, and the admin list/detail endpoints. Filtering on
|
|
196
|
+
the admin list endpoints uses repeated `?metadata=key:value` query params,
|
|
197
|
+
AND-combined; see `docs/agent-runs.md` §"Web UI" for details.
|
|
198
|
+
|
|
199
|
+
## 5. One-shot runs
|
|
200
|
+
|
|
201
|
+
```
|
|
202
|
+
POST /api/v1/workspaces/{slug}/agent-runs
|
|
203
|
+
GET /api/v1/workspaces/{slug}/agent-runs/{runId}
|
|
204
|
+
GET /api/v1/workspaces/{slug}/agent-runs/{runId}/stream
|
|
205
|
+
POST /api/v1/workspaces/{slug}/agent-runs/{runId}/tool-results
|
|
206
|
+
POST /api/v1/workspaces/{slug}/agent-runs/{runId}/cancel
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
`POST /agent-runs` returns `202 Accepted` immediately:
|
|
210
|
+
|
|
211
|
+
```json
|
|
212
|
+
{ "runId": "run_abc", "streamUrl": "/api/v1/workspaces/acme/agent-runs/run_abc/stream" }
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
`GET .../stream` is the canonical event channel; see §7.
|
|
216
|
+
|
|
217
|
+
`GET /agent-runs/{runId}` returns the run snapshot (status, final text, error,
|
|
218
|
+
spec) without subscribing to live events. Useful for polling long runs.
|
|
219
|
+
|
|
220
|
+
## 6. Sessions
|
|
221
|
+
|
|
222
|
+
```
|
|
223
|
+
POST /api/v1/workspaces/{slug}/agent-sessions
|
|
224
|
+
GET /api/v1/workspaces/{slug}/agent-sessions/{sessionId}
|
|
225
|
+
POST /api/v1/workspaces/{slug}/agent-sessions/{sessionId}/messages
|
|
226
|
+
DELETE /api/v1/workspaces/{slug}/agent-sessions/{sessionId}
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
`POST /agent-sessions` creates a session with the agent spec. The body shape
|
|
230
|
+
is the same as the one-shot agent spec (no `prompt`, no `messages`).
|
|
231
|
+
Returns:
|
|
232
|
+
|
|
233
|
+
```json
|
|
234
|
+
{ "sessionId": "ses_abc" }
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
`POST /agent-sessions/{id}/messages` queues a new run scoped to the session
|
|
238
|
+
and returns `{ runId, streamUrl }` just like a one-shot run. Body:
|
|
239
|
+
|
|
240
|
+
```jsonc
|
|
241
|
+
{
|
|
242
|
+
"prompt": "What's in /etc/hosts?",
|
|
243
|
+
"tools": [/* optional refresh of tool definitions */]
|
|
244
|
+
}
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
The server prepends the session's prior messages, runs the model, and on
|
|
248
|
+
success appends the new user/assistant turns back to the session row. Local
|
|
249
|
+
tool **handlers** are *not* persisted: the session stores definitions
|
|
250
|
+
(name, schema, description) so that a restarted SDK can re-bind handlers and
|
|
251
|
+
keep going.
|
|
252
|
+
|
|
253
|
+
`DELETE` flips the session to `ended` and cancels any in-flight run.
|
|
254
|
+
|
|
255
|
+
## 7. SSE stream
|
|
256
|
+
|
|
257
|
+
`GET .../agent-runs/{runId}/stream` returns `text/event-stream`. Reconnects
|
|
258
|
+
support both:
|
|
259
|
+
|
|
260
|
+
- the standard `Last-Event-ID` request header (set automatically by browser
|
|
261
|
+
`EventSource`), and
|
|
262
|
+
- a `?lastSeq=<int>` query param (preferred for bare HTTP clients).
|
|
263
|
+
|
|
264
|
+
The server replays missed events from the `EphemeralAgentRunEvent` table in
|
|
265
|
+
order before resuming the live tail.
|
|
266
|
+
|
|
267
|
+
Each frame:
|
|
268
|
+
|
|
269
|
+
```
|
|
270
|
+
id: 17
|
|
271
|
+
event: <type>
|
|
272
|
+
data: <utf-8 JSON>
|
|
273
|
+
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
`<type>` and `<data>` shapes:
|
|
277
|
+
|
|
278
|
+
```jsonc
|
|
279
|
+
// running message
|
|
280
|
+
{ "seq": 1, "type": "started", "data": {} }
|
|
281
|
+
|
|
282
|
+
// streamed assistant tokens (zero or more per turn)
|
|
283
|
+
{ "seq": 2, "type": "assistant_delta", "data": { "text": "Hello" } }
|
|
284
|
+
|
|
285
|
+
// completed assistant message (text + any tool calls about to execute)
|
|
286
|
+
{ "seq": 3, "type": "assistant_message", "data": { "text": "...", "toolCalls": [...] } }
|
|
287
|
+
|
|
288
|
+
// server-side tool call/result (informational; SDK does not act on these)
|
|
289
|
+
{ "seq": 4, "type": "tool_call", "data": { "toolUseId": "...", "name": "...", "input": {...} } }
|
|
290
|
+
{ "seq": 5, "type": "tool_result", "data": { "toolUseId": "...", "name": "...", "ok": true, "summary": "..." } }
|
|
291
|
+
|
|
292
|
+
// LOCAL tool call — SDK MUST POST a tool-result for the same toolUseId
|
|
293
|
+
{ "seq": 6, "type": "local_tool_call", "data": { "toolUseId": "tu_x", "name": "read_file", "input": { "path": "/etc/hosts" } } }
|
|
294
|
+
|
|
295
|
+
// echo of the SDK's POSTed tool-result, persisted for replay
|
|
296
|
+
{ "seq": 7, "type": "local_tool_result_in", "data": { "toolUseId": "tu_x", "output": "127.0.0.1 ..." } }
|
|
297
|
+
|
|
298
|
+
// terminal event
|
|
299
|
+
{ "seq": 8, "type": "result", "data": { "subtype": "success", "text": "Final reply" } }
|
|
300
|
+
{ "seq": 8, "type": "result", "data": { "subtype": "error_local_tool_timeout", "error": "..." } }
|
|
301
|
+
{ "seq": 8, "type": "cancelled", "data": {} }
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
A run terminates with exactly one of `result` or `cancelled`. The connection
|
|
305
|
+
is closed by the server immediately after sending the terminal event. Clients
|
|
306
|
+
should not assume any particular ordering between the human-readable `event:`
|
|
307
|
+
field and the parsed `type` inside `data` — they are always equal, but
|
|
308
|
+
implementations should rely on `data.type` because some HTTP middleware
|
|
309
|
+
strips the `event:` line.
|
|
310
|
+
|
|
311
|
+
## 8. Local tool result
|
|
312
|
+
|
|
313
|
+
```
|
|
314
|
+
POST /api/v1/workspaces/{slug}/agent-runs/{runId}/tool-results
|
|
315
|
+
Content-Type: application/json
|
|
316
|
+
|
|
317
|
+
{
|
|
318
|
+
"toolUseId": "tu_x",
|
|
319
|
+
"result": "127.0.0.1 localhost" // OR
|
|
320
|
+
"error": "ENOENT: no such file"
|
|
321
|
+
}
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
`200 OK` on accept. `404 unknown_tool_use` if the `toolUseId` is unknown,
|
|
325
|
+
already satisfied, or the run has terminated. `409 run_terminal` if the run
|
|
326
|
+
has already produced a `result` event.
|
|
327
|
+
|
|
328
|
+
`result` MUST be a string; SDKs serialize structured outputs as JSON before
|
|
329
|
+
posting. Errors are surfaced to the model as a tool-error response.
|
|
330
|
+
|
|
331
|
+
## 9. Cancellation
|
|
332
|
+
|
|
333
|
+
```
|
|
334
|
+
POST /api/v1/workspaces/{slug}/agent-runs/{runId}/cancel
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
Idempotent. The run will produce a terminal `cancelled` event on the SSE
|
|
338
|
+
stream. In-flight `tool-results` posted after cancellation are accepted with
|
|
339
|
+
`200 OK` but ignored.
|
|
340
|
+
|
|
341
|
+
## 10. Errors
|
|
342
|
+
|
|
343
|
+
All non-2xx responses use this body shape:
|
|
344
|
+
|
|
345
|
+
```jsonc
|
|
346
|
+
{
|
|
347
|
+
"error": "invalid_model", // machine-readable code
|
|
348
|
+
"message": "Model 'foo' is ambiguous; pick one of: provider:cm6...",
|
|
349
|
+
"candidates": [/* sometimes present */]
|
|
350
|
+
}
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
Common codes:
|
|
354
|
+
|
|
355
|
+
| Code | HTTP | Notes |
|
|
356
|
+
| ---------------------- | ---: | ----- |
|
|
357
|
+
| `unauthorized` | 401 | Missing/invalid API key |
|
|
358
|
+
| `not_found` | 404 | Workspace, run, or session unknown |
|
|
359
|
+
| `invalid_request` | 400 | Body failed Zod validation |
|
|
360
|
+
| `invalid_model` | 400 | `modelId` couldn't be resolved |
|
|
361
|
+
| `unknown_tool_use` | 404 | Tool-result for an unknown `toolUseId` |
|
|
362
|
+
| `run_terminal` | 409 | Tool-result after run finished |
|
|
363
|
+
| `rate_limited` | 429 | Per-API-key sliding window |
|
|
364
|
+
|
|
365
|
+
## 11. Suggested client architecture
|
|
366
|
+
|
|
367
|
+
A reference SDK should:
|
|
368
|
+
|
|
369
|
+
1. Hold the API key + workspace slug and a small `fetch` (or stdlib HTTP)
|
|
370
|
+
client.
|
|
371
|
+
2. Maintain a registry of local tool handlers, keyed by `name`.
|
|
372
|
+
3. On `runAgent` / `session.send`:
|
|
373
|
+
- POST the run/message, get `{ runId, streamUrl }`.
|
|
374
|
+
- Open the SSE stream with `Last-Event-ID` if reconnecting.
|
|
375
|
+
- On `local_tool_call`, look up the handler, validate args against the
|
|
376
|
+
tool's schema, run it, POST the result back to `.../tool-results`.
|
|
377
|
+
- On terminal `result`, resolve the call. On `error` subtype, throw.
|
|
378
|
+
4. Re-emit assistant deltas/events as a stream/iterator for callers who care
|
|
379
|
+
about live output.
|
|
380
|
+
5. Treat the protocol as the contract. Implementation details such as Valkey
|
|
381
|
+
pub/sub or pgvector are server-side only.
|
|
382
|
+
|
|
383
|
+
The TypeScript SDK in [`packages/mantyx-sdk/ts/`](../packages/mantyx-sdk/ts/) and the Go SDK in
|
|
384
|
+
[`packages/mantyx-sdk/go/`](../packages/mantyx-sdk/go/) are reference implementations of this protocol.
|
package/package.json
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@mantyx/sdk",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "MANTYX as a hosted agent runtime: define ephemeral agents, mix server-side MANTYX tools with locally-executed tools, run them remotely.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js",
|
|
13
|
+
"require": "./dist/index.cjs"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist",
|
|
18
|
+
"docs",
|
|
19
|
+
"README.md",
|
|
20
|
+
"LICENSE",
|
|
21
|
+
"CHANGELOG.md",
|
|
22
|
+
"CONTRIBUTING.md",
|
|
23
|
+
"EXTRACT.md"
|
|
24
|
+
],
|
|
25
|
+
"scripts": {
|
|
26
|
+
"sync-version": "node ../scripts/sync-version.mjs",
|
|
27
|
+
"build": "tsup src/index.ts --format esm,cjs --dts --clean --sourcemap",
|
|
28
|
+
"dev": "tsup src/index.ts --format esm,cjs --dts --watch",
|
|
29
|
+
"typecheck": "tsc --noEmit",
|
|
30
|
+
"test": "vitest run",
|
|
31
|
+
"test:watch": "vitest",
|
|
32
|
+
"prepublishOnly": "npm run build"
|
|
33
|
+
},
|
|
34
|
+
"engines": {
|
|
35
|
+
"node": ">=18.17.0"
|
|
36
|
+
},
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"zod": "^3.23.8"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@types/node": "^22.0.0",
|
|
42
|
+
"tsup": "^8.3.0",
|
|
43
|
+
"typescript": "^5.6.0",
|
|
44
|
+
"vitest": "^2.1.0"
|
|
45
|
+
},
|
|
46
|
+
"keywords": [
|
|
47
|
+
"mantyx",
|
|
48
|
+
"agent",
|
|
49
|
+
"llm",
|
|
50
|
+
"sdk",
|
|
51
|
+
"tool-use",
|
|
52
|
+
"agentic",
|
|
53
|
+
"ai"
|
|
54
|
+
],
|
|
55
|
+
"license": "Apache-2.0",
|
|
56
|
+
"publishConfig": {
|
|
57
|
+
"access": "public"
|
|
58
|
+
}
|
|
59
|
+
}
|