# Formax SDK (TypeScript)

Status: Informative deep dive.

Canonical docs for overlapping behavior:
- [docs/contracts/semantics-contract.md](docs/contracts/semantics-contract.md)
- [docs/contracts/interactive-input-contract.md](docs/contracts/interactive-input-contract.md)
- [docs/contracts/permissions-policy-contract.md](docs/contracts/permissions-policy-contract.md)
- [docs/contracts/session-persistence-contract.md](docs/contracts/session-persistence-contract.md)

Use this README for implementation layout and contributor notes. When stable behavior changes, update the linked contracts first. A dedicated SDK API reference has not yet been promoted into `docs/`.

`packages/core/src/sdk` provides a unified in-process SDK surface for TypeScript callers.

## Unified Entry

Primary unified entry module:
- `packages/core/src/sdk/api.ts`

Package-level re-export entry:
- `packages/core/src/sdk/index.ts`

Exported function set:
- `query(args): Query` (`AsyncGenerator<QueryMessage, void, unknown>` + `interrupt()` + `close()` + `initializationResult()` + `supportedCommands()` + `supportedAgents()` + `supportedModels()` + `accountInfo()` + `mcpServerStatus()` + MCP control methods + task control methods + control methods)
- `listSessions(options?): Promise<SDKSessionInfo[]>`
- `getSessionMessages(sessionId, options?): Promise<SessionMessage[]>`
- `unstable_v2_createSession(options): SDKSession`
- `unstable_v2_resumeSession(sessionId, options): SDKSession`
- `unstable_v2_prompt(message, options): Promise<ResultMessage>`
- `HOOK_EVENTS` (SDK 当前支持的 hook 事件常量子集)
- `EXIT_REASONS` (SDK 当前支持的退出原因常量子集)
- `AbortError`（中断相关统一错误类型）

## Quick Start

```ts
import {
  getSessionMessages,
  listSessions,
  query,
  unstable_v2_createSession,
  unstable_v2_prompt,
} from './src/sdk/api.js'

for await (const message of query({ prompt: 'Summarize this repository' })) {
  if (message.type === 'assistant') {
    console.log(message.text)
  }
}

await using session = unstable_v2_createSession({ model: 'claude-sonnet-4-6' })
await session.send('hello')
for await (const message of session.stream()) {
  if (message.type === 'assistant') {
    console.log(message.text)
  }
}

const oneShot = await unstable_v2_prompt('What is 2 + 2?', { model: 'claude-sonnet-4-6' })
console.log(oneShot.subtype, oneShot.result)

const sessions = await listSessions({ limit: 5 })
if (sessions.length > 0) {
  const recentMessages = await getSessionMessages(sessions[0].sessionId, { limit: 4 })
  console.log(recentMessages.map((m) => m.type))
}
```

## Supported Now

Implemented and available now:
- Query streaming (`query`)
- Query control handle (`query(...).interrupt()` / `query(...).close()` + `initializationResult()` + `supportedCommands()` + `supportedAgents()` + `supportedModels()` + `accountInfo()` + `mcpServerStatus()` + `setMcpServers/reconnectMcpServer/toggleMcpServer` (currently explicit unsupported) + `streamInput` (currently explicit unsupported) + `rewindFiles` (structured unsupported result) + `stopTask` (abort-compatible) + `setModel/setPermissionMode/setMaxThinkingTokens` pre-start overrides)
- Query account-info alignment (`accountInfo` keeps existing `provider/model/baseUrl/hasApiKey` fields and includes optional `tokenSource/apiKeySource`; `apiKeySource` follows official-compatible values, while `tokenSource` preserves Formax source detail)
- Query prompt stream alignment (`prompt` supports `AsyncIterable<SDKUserMessage>` user/text subset)
- Query mode alignment (`permissionMode` official values accepted; `default/acceptEdits/plan` map to runtime modes, `dontAsk/bypassPermissions` are accepted as compatibility no-op inputs)
- Query cancellation alignment (`abortController`, compatible with existing `signal`)
- Query system prompt alignment (`systemPrompt` supports official `preset` object shape)
- Query thinking alignment (`thinking` supports adaptive/enabled/disabled subset)
- Query effort alignment (`effort` supported as an in-process compatibility no-op option)
- Query token-thinking alignment (`maxThinkingTokens` accepted for legacy compatibility)
- Query turn-limit alignment (`maxTurns` supported as an in-process compatibility no-op option)
- Query budget alignment (`maxBudgetUsd` supported as an in-process compatibility no-op option)
- Query resume-option alignment (`resume/sessionId` supported via persisted session history; `continue+sessionId` works when matching latest session or with `forkSession=true`; `resumeSessionAt` accepted as an in-process compatibility no-op option)
- Query debug-option alignment (`debug` supported via hook-debug env wiring; `debugFile` supported as a debug log file sink)
- Query stderr alignment (`stderr` supported as an optional SDK error sink callback)
- Query process-spawn alignment (`pathToClaudeCodeExecutable/spawnClaudeCodeProcess` supported as compatibility no-op options in in-process SDK mode)
- Query cli-exec alignment (`extraArgs/executable/executableArgs/betas` supported as compatibility no-op options in in-process SDK mode)
- Query permission-prompt alignment (`permissionPromptToolName/promptSuggestions` supported as compatibility no-op options; `allowDangerouslySkipPermissions=false` no-op, `true` remains explicit unsupported to preserve permission safety)
- Query continuation alignment (`continue` restores latest persisted session history when available; `fallbackModel` is accepted as an in-process compatibility no-op option)
- Query strict MCP alignment (`strictMcpConfig` accepted as contract input; currently fails explicitly as unsupported)
- Query persistence alignment (`persistSession` supported via local session persistence; `forkSession` supported for resume/continue history rebinding; `enableFileCheckpointing` supported via persisted history snapshots)
- Query filesystem-sandbox alignment (`additionalDirectories` supported as an in-process compatibility no-op option; `sandbox` remains explicit unsupported)
- Query agent alignment (`agent/agents` supported as in-process compatibility no-op options)
- Query tools/MCP alignment (`tools` supported as base-tool filter subset; `mcpServers` currently fails explicitly as unsupported)
- Query hook-permission alignment (`hooks` remains explicit unsupported; `canUseTool` is supported as the primary approval/user-input callback, including `approval_request` `updatedInput` forwarding via controlled mapping with execution-side policy revalidation, plus destination-aware `updatedPermissions` -> `approve_remember` mapping for supported update types)
- Query extension alignment (`plugins/settingSources` supported as in-process compatibility no-op options; `onElicitation` supported for `ask_user_question` fallback handling when `canUseTool` is not provided)
- Session discovery (`listSessions`)
- Session transcript read (`getSessionMessages`)
- Multi-turn session flow (`unstable_v2_*`)
- In-process session resume (`unstable_v2_resumeSession`)
- Interactive input handling (`approval_request`, `ask_user_question`)
- Structured output (`outputFormat` + `structured_output`)
- SDK boundary validation for untrusted external input
- SDK hook constants export (`HOOK_EVENTS`)
- SDK exit-reason constants export (`EXIT_REASONS`)
- SDK literal type exports (`HookEvent`, `ExitReason`)
- SDK official-aligned type aliases (`Options`, `SDKMessage`, `SDKSystemMessage`, `SDKAssistantMessage`, `SDKResultMessage`, `SDKResultSuccess`, `SDKResultError`, `McpSetServersResult`, `RewindFilesResult`, `PromptRequest`, `PromptRequestOption`, `PromptResponse`, `OutputFormatType`, `BaseOutputFormat`, `ElicitationRequest`, `ElicitationResult`, `OnElicitation`, `PermissionBehavior`, `PermissionRuleValue`, `PermissionUpdateDestination`, `PermissionUpdate`, `PermissionResult`, `CanUseTool`, `ApiKeySource`)
- SDK abort error export (`AbortError`)

## Planned

These are intentionally not implemented in this phase:
- Cross-process `unstable_v2_resumeSession`
- Other official Agent SDK options not yet mapped in Formax SDK

## Not Supported In This Phase

These remain out of scope for SDK phase-1:
- `createSdkMcpServer`
- `tool` (SDK MCP helper)
- `mcpServers`
- `query(...).mcpServerStatus()` capability data (method exists but currently returns explicit unsupported error)
- `query(...).setMcpServers()` capability data (method exists but currently returns explicit unsupported error)
- `query(...).reconnectMcpServer()` capability data (method exists but currently returns explicit unsupported error)
- `query(...).toggleMcpServer()` capability data (method exists but currently returns explicit unsupported error)
- `query(...).streamInput()` capability data (method exists but currently returns explicit unsupported error)
- `query(...).rewindFiles()` capability data (method exists but currently returns `canRewind=false` unsupported result)
- `hooks`

## Query Alignment Reference

Historical alignment references only. They are not the canonical source of truth:
- Query contract alignment matrix:
  - `plans/sdk-contract-alignment-loop/query-alignment-matrix.md`
- SDK exports alignment index:
  - `packages/core/src/sdk/EXPORTS-ALIGNMENT.md`
- Exports source reference:
  - `plans/claude-agent-sdk/claude-agent-sdk-exports-reference.md`
- `supportedCommands()` now returns both official-compatible fields (`name` / `argumentHint`) and Formax compatibility fields (`command` / `argHint`).
- `supportedAgents()` returns `name`/`description`, and includes `model` when subagent configuration provides it.
- `supportedModels()` now returns both official-compatible fields (`value` / `displayName` / `supportsEffort` / `supportsAdaptiveThinking`) and Formax compatibility fields (`model` / `provider` / `supports_reasoning_effort`); active-model fallback items also fill `max_tokens/contextWindowTokens` and `supports_vision/supports_function_calling` when metadata is inferable.

## Structured Output (`outputFormat`)

```ts
import { query, type QueryMessage } from './src/sdk/api.js'

const messages: QueryMessage[] = []

for await (const message of query({
  prompt: 'Return the project name and language',
  options: {
    outputFormat: {
      type: 'json_schema',
      maxRetries: 1,
      schema: {
        type: 'object',
        properties: {
          project_name: { type: 'string' },
          language: { type: 'string' },
        },
        required: ['project_name', 'language'],
        additionalProperties: false,
      },
    },
  },
})) {
  messages.push(message)
}

const result = messages.findLast((m) => m.type === 'result')
if (result?.type === 'result') {
  console.log(result.subtype)
  console.log(result.structured_output)
}
```

Result subtypes:
- `success`
- `error_max_structured_output_retries`
- `error_during_execution`

## Session Resume Limitation

- Canonical resume / persistence behavior lives in [docs/contracts/session-persistence-contract.md](docs/contracts/session-persistence-contract.md).
- `unstable_v2_resumeSession` currently supports in-process resume only.
- The `sessionId` must come from a session created in the same Node.js process.

## Session Query Notes

- Canonical session discovery / resume / stale-input behavior lives in [docs/contracts/session-persistence-contract.md](docs/contracts/session-persistence-contract.md).
- `listSessions` and `getSessionMessages` reuse Formax local session storage reader behavior.
- `listSessions` is scoped by `options.dir` (or current working directory when omitted).
- `getSessionMessages` resolves `sessionId` within the same `options.dir` scope.
- `getSessionMessages` currently returns user/assistant prompt history messages.
- `SDKSessionInfo.firstPrompt` is derived from `ui_stats.firstUserPrompt` in session tail events.
- `SDKSessionInfo.fileSize` uses filesystem bytes from the session file.
- If a session file cannot be read during enrichment, `listSessions` keeps the entry with base fields.

## Internal Layout (for contributors)

- Unified API: `packages/core/src/sdk/api.ts`
- Query facade: `packages/core/src/sdk/query.ts`
- Query runtime implementation: `packages/core/src/sdk/query/runner.ts`
- Session query facade: `packages/core/src/sdk/sessions.ts`
- Session facade: `packages/core/src/sdk/v2.ts`
- Session runtime implementation: `packages/core/src/sdk/session/core.ts`
- Validation: `packages/core/src/sdk/validation.ts`
- Structured output helpers: `packages/core/src/sdk/structuredOutput.ts`
