Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Python SDK reference

The Python SDK is part of the wisp-ai distribution and carries a py.typed marker. It requires Python 3.12+. The package-boundary evaluation in #409 has not produced a separate client or SDK distribution.

For lifecycle guidance and complete examples, start with the Python SDK guide.

Supported namespaces

NamespaceSupported surface
wisp.sdkIn-process controller and startup options
wisp.rpcHigh-level controller, transport protocol, subprocess transport, and typed command models
wisp.eventsTyped event models and JSON/dict parsers
wisp.configImmutable runtime configuration
wisp.sessionsJSONL session store, entries, replay models, and typed session errors
wisp.runtimeStatic extension/runtime contracts and registries
wisp.providersProvider event/tool types, built-in providers, and deterministic fake providers
wisp.toolsTool, context, result, safety, approval, and policy contracts

Import supported names from these namespaces, not private implementation modules. Package-level __all__ lists are verified from the built wheel. wisp.events re-exports every event model from its package root; import from wisp.events, not its lifecycle submodules. The stable entry points used by SDK consumers are described below.

See Project file discovery for snapshot bounds, cancellation, and handling ProjectFilesInvalidated notifications.

wisp.sdk

InProcessWisp

class InProcessWisp(RpcController):
    @classmethod
    async def start(
        cls,
        config: WispConfig,
        *,
        options: InProcessOptions | None = None,
    ) -> InProcessWisp: ...

    @classmethod
    async def from_environment(
        cls,
        *,
        provider: str | None = None,
        model: str | None = None,
        session_dir: Path | None = None,
        auth_path: Path | None = None,
        options: InProcessOptions | None = None,
    ) -> InProcessWisp: ...

    async def aclose(self) -> None: ...

InProcessWisp inherits every command, event, and close() method from RpcController. It is an async context manager; __aexit__ calls aclose().

The active AnyIO backend must be asyncio. Startup on another backend raises RuntimeError before runtime resources are retained.

events() returns the controller’s only AsyncIterator[KnownWispEvent]. A second call raises RuntimeError.

InProcessOptions

InProcessOptions is an immutable dataclass.

FieldTypeDefaultMeaning
all_toolsboolFalseExpose the complete registered tool set to the model.
allow_read_toolsboolFalseExpose tools classified as read-only.
allowed_toolstuple[str, ...]()Expose selected tool names.
resumestr | NoneNoneResume by session path, filename, ID, or unique prefix.
continue_latestboolFalseResume the newest session in the configured store.
approve_unsafe_toolsboolFalsePre-approve mutating and command tools.
max_tool_iterationsint | NoneNoneOptional non-negative model/tool round limit.
startup_trustedboolFalseCaller-supplied initial project trust decision.
project_context_rootPath | NoneNoneRoot for trust, project settings, skills, and instructions.
cwdPath | NoneNoneWorking directory for built-in file and process tools.

resume and continue_latest cannot both be set. A negative max_tool_iterations is rejected. When cwd is omitted and project_context_root is supplied, the project root also becomes the tool working directory.

Tool visibility does not imply tool approval. Unsafe tools still request approval unless approve_unsafe_tools=True.

wisp.rpc

RpcTransport

A custom transport implements this public protocol:

class RpcTransport(Protocol):
    async def send(self, command: RpcCommand) -> None: ...
    def events(self) -> AsyncIterator[KnownWispEvent]: ...
    async def close(self) -> None: ...

Transport implementations preserve typed command submission and one ordered event stream. Runtime policy remains in the shared command host, not in the transport.

RpcController

RpcController(
    transport: RpcTransport,
    *,
    command_id_factory: Callable[[str], str] | None = None,
)

Every command method returns str, the selected command ID, after the transport accepts the typed request. Completion and results arrive through events().

Prompt, lifecycle, configuration, and snapshots

MethodSignature after selfResult event or effect
prompt(prompt: str, *, command_id: str | None = None)Agent/message/tool events; terminal RpcCommandFinished
init(*, command_id: str | None = None)Initialize project guidance
compact(instructions: str | None = None, *, command_id: str | None = None)Compaction events
configure(*, provider=None, model=None, effort=None, clear_effort=False, auto_compaction_enabled=None, mode=None, command_id=None)Configuration events and terminal status
get_model_catalog(*, command_id: str | None = None)RpcModelCatalogReported
get_connection_catalog(*, command_id: str | None = None)RpcConnectionCatalogReported
store_api_key(provider: str, api_key: str, *, command_id: str | None = None)Terminal RpcCommandFinished; refreshed RpcConnectionCatalogReported when available
disconnect_provider(provider: str, *, command_id: str | None = None)Terminal RpcCommandFinished; refreshed RpcConnectionCatalogReported when available
begin_device_code(provider: str, *, command_id: str | None = None)RpcDeviceCodeReported, zero or more RpcDeviceCodeProgressReported, terminal RpcCommandFinished, and refreshed RpcConnectionCatalogReported when available
get_session_stats(*, command_id: str | None = None)SessionStatsReported
get_permissions(*, command_id: str | None = None)RpcPermissionsReported
set_permissions(mode: PermissionMode, *, command_id: str | None = None)Saved project default, then RpcPermissionsReported
get_state(*, command_id: str | None = None)RpcStateReported
get_commands(*, command_id: str | None = None)RpcCommandsReported
get_project_files(*, command_id: str | None = None)RpcProjectFilesReported
get_skills(*, command_id: str | None = None)RpcSkillsReported
get_mcp_status(*, command_id: str | None = None)RpcMcpStatusReported
shutdown(*, command_id: str | None = None)Request host shutdown

For credential mutations, the terminal RpcCommandFinished is authoritative for the mutation itself. A successful mutation normally emits a correlated refreshed connection catalog first. If that secondary refresh fails, the backend emits a sanitized ErrorEvent diagnostic but still finishes the mutation with ok=true; clients must not retry a completed credential write or delete solely because its status refresh was unavailable.

configure() accepts provider: str | None, model: str | None, effort: str | None, auto_compaction_enabled: bool | None, and mode: Literal["build", "plan"] | None. Pass the string "build" for normal operation or "plan" for read-only planning. effort=None leaves the current setting untouched; use clear_effort=True to restore the provider default.

Live queues and cancellation

MethodSignature after self
steer(content: str, *, command_id: str | None = None)
follow_up(content: str, *, command_id: str | None = None)
get_queue_state(*, command_id: str | None = None)
set_queue_mode(kind: QueueKind, mode: QueueMode, *, command_id: str | None = None, expected_token: str | None = None)
pop_queue(kind: QueueKind, *, command_id: str | None = None, expected_token: str | None = None)
clear_queue(kind: QueueKind | None = None, *, command_id: str | None = None, expected_token: str | None = None)
cancel(target_id: str, *, command_id: str | None = None)

Connection catalogs and progress events contain sanitized status only. API keys and OAuth tokens are write-only inputs and never appear in results, events, or session JSONL. RpcDeviceCodeReported intentionally exposes the short-lived user code and verification URL needed to complete login; clients must display them without logging or persistence.

QueueKind is "steering" | "follow_up". QueueMode is "one_at_a_time" | "all". cancel() targets a running prompt or compaction command ID.

QueueUpdated.token is an opaque revision of an active queue owner; retained/legacy snapshots have no token. Pass it as expected_token to guard a displayed mode/pop/clear operation. Any intervening mutation or a replacement run invalidates it, including removal and replacement with identical text. Stale guards fail through the ordinary command lifecycle without mutation. Omitting the guard retains legacy command semantics. Inspection works while idle; mutations still require an active run.

Solicited QueueUpdated events carry command_id; spontaneous run events omit it. Successful removals publish QueueItemsRemoved, then QueueUpdated, then RpcCommandFinished. JSONL response-size preflight rejects oversized removal payloads before mutation. Transport loss after execution is not a transaction rollback: do not blindly retry a pop or clear after an unknown outcome.

Trust and approval

MethodSignature after self
trust(request_id: str, *, trusted: bool, reason: str | None = None, transient: bool = False, command_id: str | None = None)
approve(call_id: str, *, approved: bool = True, reason: str | None = None, scope: ApprovalScope | None = None, command_id: str | None = None)

ApprovalScope is "once" | "tool_session" | "all_session" | "all_project". all_project saves YOLO for the current project; the other scopes remain temporary. PermissionMode is "ask" | "yolo". set_permissions saves the default and clears temporary grants; it fails without changing permissions if an operation is running or saving fails. Saved defaults apply to future launches in the same canonical project directory. Match request_id from TrustRequested and call_id from ToolApprovalRequested. These methods are re-entrant control commands and may be submitted while a prompt is paused.

Persisted sessions

MethodSignature after selfPrimary result event
get_sessions(*, limit: int = 50, query: str = "", cursor: str | None = None, command_id: str | None = None)RpcSessionsReported
new_session(*, command_id: str | None = None)Deselect; next prompt creates a session
select_session(session_id: str, *, command_id: str | None = None)RpcSessionSelected
set_session_name(name: str, *, session_id: str | None = None, command_id: str | None = None)RpcSessionNameChanged
clone_session(*, command_id: str | None = None)RpcSessionCloned
fork_session(entry_id: str, *, command_id: str | None = None)RpcSessionForked
get_session_tree(*, limit: int = 200, after_entry_id: str | None = None, command_id: str | None = None)RpcSessionTreeReported
navigate_session_tree(entry_id: str, *, command_id: str | None = None)RpcSessionTreeNavigated
unrevert_session_tree(*, command_id: str | None = None)RpcSessionTreeUnreverted

get_sessions() accepts limit from 0 through 200 (zero returns no rows or cursors). query is bounded to 1024 UTF-8 bytes and uses trimmed, Unicode-casefolded literal substring matching against current session names and full IDs. An empty query lists all sessions; unnamed sessions remain searchable by ID. Duplicate names remain separate identities.

RpcSessionsReported includes the normalized query, next_cursor, and previous_cursor. Pass a non-null cursor back with the same query to navigate. Cursors are opaque, bounded to 4096 bytes, and tied to the catalog’s current file metadata; edits, renames, additions, or deletions can invalidate them. On a stale-cursor failure, retry without a cursor. Do not decode a cursor, reuse it with another query/store, or infer a total count from the page size. Reports can be smaller than requested to fit transport limits; their cursors still follow the actual returned boundary. An individually oversized summary fails instead of being silently skipped.

Results are ordered newest-first by modification time, then filename descending as a deterministic tie-breaker. Search reads session metadata, not full transcript models, but scan cost still grows with the catalog and JSONL files; bounded result pages are not a durable index or constant-time search. Selected-session metadata is independent of the query. Existing calls without query/cursor retain their behavior. The shared get_sessions command and rpc.sessions event use additive optional fields in live RPC v9; historical protocol bundles and persisted records are unchanged.

Session tree pages accept 1 through 500 nodes.

Transcript pages use:

async def get_messages(
    *,
    session_id: str | None = None,
    limit: int = 200,
    before_entry_id: str | None = None,
    after_entry_id: str | None = None,
    entry_ids: tuple[str, ...] = (),
    complete_structure: bool = False,
    full_content: bool = False,
    allow_during_prompt: bool = False,
    command_id: str | None = None,
) -> str: ...

limit is 1 through 500. Forward and backward cursors are mutually exclusive. Exact entry_ids cannot be combined with cursors; at most 16 IDs are accepted. full_content=True requires exactly one explicit entry ID. The result is RpcMessagesReported, including bounded message snapshots, truncated, and continuation cursors.

Event and cleanup methods

def events(self) -> AsyncIterator[KnownWispEvent]: ...
async def close(self) -> None: ...

The controller delegates stream ownership and cleanup to its transport. InProcessWisp.aclose() is an alias for its client cleanup path.

JsonlSubprocessRpcTransport

await JsonlSubprocessRpcTransport.start(
    command: Sequence[str] | None = None,
    *,
    cwd: Path | None = None,
    env: Mapping[str, str] | None = None,
    stderr: int | None = subprocess.DEVNULL,
) -> JsonlSubprocessRpcTransport

The default command is the current Python interpreter running -m wisp --mode rpc. Commands are newline-delimited JSON on stdin; stdout is parsed into typed events. Keep the default stderr=subprocess.DEVNULL: the current transport does not expose a public stderr stream, so selecting subprocess.PIPE can block a noisy child and is not a supported SDK integration pattern.

close() closes stdin, waits for bounded graceful exit, then terminates and kills if needed. It is idempotent and re-raises a retained close failure.

Typed command models

wisp.rpc exports RpcCommand and one Pydantic model for each command:

  • work/configuration: PromptCommand, InitCommand, CompactCommand, ConfigureCommand, ShutdownCommand
  • snapshots: GetSessionStatsCommand, GetStateCommand, GetCommandsCommand, GetSkillsCommand, GetMcpStatusCommand, GetProjectFilesCommand
  • queue/control: SteerCommand, FollowUpCommand, GetQueueStateCommand, SetQueueModeCommand, PopQueueCommand, ClearQueueCommand, CancelCommand
  • safety: ApprovalCommand, TrustCommand
  • sessions: GetMessagesCommand, GetSessionsCommand, NewSessionCommand, SelectSessionCommand, SetSessionNameCommand, CloneSessionCommand, ForkSessionCommand, GetSessionTreeCommand, NavigateSessionTreeCommand, UnrevertSessionTreeCommand

Each model is frozen, discriminated by type, accepts an optional id, and provides to_json_line(). Prefer RpcController unless an integration is implementing a lower-level transport.

wisp.events

Core types and parsers

NameContract
WispEventFrozen Pydantic base model with type and timestamp. Unknown fields are rejected.
KnownWispEventDiscriminated union of every event model understood by this version.
wisp_event_from_json(line)Validate one JSON event string and return KnownWispEvent.
wisp_event_from_dict(data)Validate one event dictionary and return KnownWispEvent.

Use the parser functions at protocol boundaries rather than selecting a model from an untrusted type value manually. Unsupported future schemas raise ValueError. See Compatibility & versioning for readable history, consumer actions, and the deprecation policy.

Event groups

GroupImportant models
Command lifecycleRpcCommandStarted, RpcCommandFinished
Assistant outputMessageStarted, MessageDelta, MessageCompleted
Run lifecycleAgentStarted, TurnStarted, TurnCompleted, AgentCompleted, ErrorEvent
SafetyTrustRequested, TrustResolved, ToolApprovalRequested, ToolApprovalResolved
ToolsToolCallRequested, ToolExecutionStarted, ToolExecutionEnded, ToolResultReady
Compaction/contextContextEstimated, ContextPressure, ContextOverflow, CompactionStarted, CompactionCompleted
QueuesQueueUpdated, QueueItemsRemoved, QueueMessageInjected
SnapshotsSessionStatsReported, RpcStateReported, RpcCommandsReported, RpcSkillsReported, RpcMcpStatusReported, RpcProjectFilesReported
SessionsSessionSaved, RpcMessagesReported, RpcSessionsReported, RpcSessionSelected, RpcSessionCloned, RpcSessionForked, RpcSessionNameChanged, RpcSessionTreeReported, RpcSessionTreeNavigated, RpcSessionTreeUnreverted

RpcCommandFinished is the terminal command correlation event:

class RpcCommandFinished(WispEvent):
    command_id: str
    command_type: str
    ok: bool
    error: str | None = None

A failed command has ok=False and an optional sanitized error. Domain result events normally precede the matching successful terminal event and carry the same command_id. Streamed run events are ordered but do not all have command IDs.

wisp.config

WispConfig is a frozen Pydantic model. Its primary fields are:

FieldTypeDefault behavior
providerstropenai-codex
modelstr | NoneProvider default
effortstr | NoneProvider/user default
session_dirPath~/.wisp/sessions
auth_pathPath~/.wisp/auth.json
protected_pathstuple[str, ...]Built-in protected globs plus sensitive settings/auth paths
retry_policyRetryPolicyBounded default retry policy
context_reserve_tokensint16384
auto_compaction_enabledboolTrue
mcp_serverstuple[McpServerConfig, ...]()
openai_compatibleOpenAICompatibleSettings | NoneNone

WispConfig.from_env(...) applies explicit arguments over environment, trusted project settings, user settings, and defaults. For SDK startup, prefer InProcessWisp.from_environment() when the project trust transition must remain re-entrant; it resolves trust before building initial project configuration.

See Configuration for every persisted field and precedence rule.

  • wisp.sessions.JsonlSessionStore and JsonlSession provide direct typed access to append-only session storage. RPC/SDK command methods are preferred when the active runtime must change session.
  • wisp.runtime.ExtensionAPI and WispRuntime describe static extension composition. The current InProcessWisp startup methods do not accept a caller-built runtime; see #402.
  • wisp.providers.FakeProvider and ScriptedProvider are deterministic provider implementations; the package also exports provider event and tool-call types. The structural provider protocol is not currently a supported package export, and arbitrary provider injection into InProcessWisp is not yet public; both belong to #402.
  • wisp.tools.Tool, ToolContext, and ToolResult are the core custom-tool contracts. Tool registration is demonstrated in the static extension example.

Current limitations

These APIs are tracked but are not part of the current reference:

  • awaitable command results and independent event subscriptions — #400
  • direct settled lifecycle/state primitives — #401
  • caller-owned runtime/provider injection — #402
  • typed prompt/context/skill/template overrides — #403
  • in-memory sessions and atomic active-session replacement — #404
  • model, authentication, and settings management — #405
  • long-running health, restart, recovery, and observability — #406