About Claude Agent SDK Go
Claude Agent SDK Go is an MCP server published by Roasbeef in the Developer Tools category: go SDK for Claude Code – build AI agents with tool use, MCP integration, and streaming.
The repository has 21 stars and 11 forks, with the last commit 9 days ago. Recent commit activity is the strongest signal that reported issues will get attention.
Install
git clone https://github.com/Roasbeef/claude-agent-sdk-goThis server has no ConduID identity, so agent calls to it are not receipted. Pin the version you install and review the source before granting it credentials.
Ask AI
Ask AI about Claude Agent SDK Go
Powered by Claude · Grounded in docs
Security checks
Static checks derived from the repository 4 days ago. Heuristics on the source, not an audit.
- ✓README presentThe repository ships documentation.
- ✓License declaredUsable under a stated license.
- ✓Tests presentA test suite exists in the repository.
- ✓Dependencies pinnedA lockfile pins dependency versions.
- !Scoped permissionsDoesn't declare a permission scope. Assume it can do anything its process can.
1 check has not been run yet.
Releases
README
claude-agent-sdk-go
A pure Go SDK for Claude's agentic capabilities.
Overview
claude-agent-sdk-go provides a native Go interface to Claude Code by managing the CLI as a subprocess. It communicates via line-delimited JSON over stdin/stdout, giving you access to Claude's tool use, extended thinking, session management, and hook system.
This repository tracks the official TypeScript Agent SDK surface through the v0.3.296 catchup work, using Go idioms where the API shape differs.
flowchart TB
subgraph SDK[Go SDK]
Client --> Protocol --> Transport
MCP[SDK MCP] <-.->|tool calls| Protocol
end
Transport <-->|stream-json| CLI[Claude CLI]
CLI <--> API[Claude API]
When you call Query() or Stream(), the SDK spawns the CLI, sends your
prompt, and yields messages as they arrive. For MCP tools, the flow includes
bidirectional control messages:
sequenceDiagram
participant App as Go App
participant CLI as Claude CLI
participant API as Claude API
App->>CLI: prompt (stdin)
CLI->>API: API request
API-->>CLI: response stream
CLI-->>App: messages (stdout)
CLI->>App: mcp_message (tool call)
App-->>CLI: mcp_response
CLI-->>App: result
Installation
go get github.com/roasbeef/claude-agent-sdk-go
Requirements:
- Go 1.23+ (for
iter.Seq) - Claude Code CLI:
npm install -g @anthropic-ai/claude-code ANTHROPIC_API_KEYorCLAUDE_CODE_OAUTH_TOKENin your environment
Quick Start
package main
import (
"context"
"fmt"
"log"
"github.com/roasbeef/claude-agent-sdk-go"
)
func main() {
client, err := claudeagent.NewClient(
claudeagent.WithSystemPrompt("You are a helpful assistant."),
)
if err != nil {
log.Fatal(err)
}
defer client.Close()
ctx := context.Background()
for msg := range client.Query(ctx, "What is the capital of France?") {
switch m := msg.(type) {
case claudeagent.AssistantMessage:
fmt.Println(m.ContentText())
case claudeagent.ResultMessage:
fmt.Printf("Cost: $%.4f\n", m.TotalCostUSD)
}
}
}
Streaming
For multi-turn conversations:
stream, _ := client.Stream(ctx)
defer stream.Close()
stream.Send(ctx, "Let's plan a trip to Japan.")
for msg := range stream.Messages() {
if m, ok := msg.(claudeagent.AssistantMessage); ok {
fmt.Println(m.ContentText())
}
}
// Continue the conversation
stream.Send(ctx, "What about Kyoto?")
Configuration
client, _ := claudeagent.NewClient(
claudeagent.WithModel("claude-sonnet-4-5-20250929"),
claudeagent.WithSystemPrompt("You are an expert Go developer."),
claudeagent.WithPermissionMode(claudeagent.PermissionModeAcceptEdits),
claudeagent.WithMaxTurns(20),
)
Settings can be supplied either as a settings file path or as inline JSON:
client, _ := claudeagent.NewClient(
claudeagent.WithSettingsPath("/path/to/settings.json"),
)
client, _ = claudeagent.NewClient(
claudeagent.WithSettings(claudeagent.Settings{
Model: "claude-sonnet-4-5-20250929",
Permissions: &claudeagent.SettingsPermissions{
Allow: []string{"Bash(git status)"},
},
}),
)
Custom Tools (MCP)
Define tools that Claude can use during conversations:
type AddArgs struct {
A int `json:"a"`
B int `json:"b"`
}
server := claudeagent.CreateMcpServer(claudeagent.McpServerOptions{
Name: "calculator",
Tools: []claudeagent.ToolRegistrar{
claudeagent.Tool("add", "Add two numbers",
func(ctx context.Context, args AddArgs) (claudeagent.ToolResult, error) {
return claudeagent.TextResult(fmt.Sprintf("%d", args.A+args.B)), nil
},
),
},
})
client, _ := claudeagent.NewClient(
claudeagent.WithMcpServer("calculator", server),
)
See MCP Tools for typed responses, binary servers, and more.
Ralph Wiggum Loop
For complex iterative tasks, use the Ralph Wiggum loop. Claude works on a task repeatedly, seeing its previous work each iteration, until it outputs a completion signal:
loop := claudeagent.NewRalphLoop(claudeagent.RalphConfig{
Task: "Implement a Redis cache layer with tests",
CompletionPromise: "TASK COMPLETE",
MaxIterations: 10,
})
for iter := range loop.Run(ctx, claudeagent.WithModel("claude-sonnet-4-5-20250929")) {
fmt.Printf("Iteration %d\n", iter.Number)
if iter.Complete {
fmt.Println("Task completed!")
break
}
}
fmt.Printf("Total cost: $%.4f\n", loop.TotalCost())
The loop intercepts session exit via a Stop hook and reinjects the task prompt
if the completion promise (<promise>TASK COMPLETE</promise>) hasn't been
detected. See Ralph Loop for details.
Documentation
For detailed guides and examples, see docs/examples/:
- MCP Tools - Integrate custom tools via Model Context Protocol
- Hooks - Intercept and modify Claude's execution
- Subagents - Define and monitor specialized agents
- Questions - Handle interactive questions from Claude
- Sessions - Persist and resume conversations
- Permissions - Control what Claude can do
- Streaming - Real-time response handling
- Skills - Filesystem-based capability extensions
- Ralph Loop - Iterative task completion pattern
- Task Lists - Multi-agent task coordination system
TypeScript SDK Parity
The SDK tracks the upstream TypeScript Agent SDK release cadence. Coverage landed across a series of catchup cycles, one per upstream release picked up.
The v0.2.119 catchup added:
- thinking effort, task budgets, debug files, extra CLI args, agent selection, and prompt/agent progress toggles
- programmatic subagents, richer hook inputs/outputs, permission updates, and MCP HTTP/SSE configuration
- stream control and introspection helpers, runtime MCP server updates, file/read-state/plugin control, and local JSONL session helpers
- explicit settings support via
WithSettingsPath,WithSettings, andWithManagedSettings
The v0.3.150 catchup added:
- per-server MCP
TimeoutandAlwaysLoadknobs across Stdio/HTTP/SSE/proxy configurations - background-tasks control protocol (
Query.BackgroundTasks) plusBackgroundTaskSummaryandSessionCronSummarypayloads onStop/SessionEndhooks - new
system/permission_deniedmessage subtype,SDKAssistantMessage.Errorenum (incl.oauth_org_not_allowed,model_not_found),ResultMessageTTFT + origin metadata, andMemoryRecallEntryorganizationscope - hook output additions:
UpdatedToolOutputonPostToolUse,TerminalSequenceOSC notifications,SuppressOriginalPromptonUserPromptSubmit, plus per-hookArgs(no-shell exec form) andContinueOnBlock - managed-org / sandbox / worktree / marketplace settings batch covering
PolicyHelper,DisableAgentView,IsolatePeerMachines,Sandbox.TLSTerminate,Worktree.BaseRef,StatusLine.HideVimModeIndicator, marketplaceskills-dir/unsupportedsource variants, and others read_fileEncoding(utf-8 / base64),applyFlagSettingsper-key null clearing, host-sidehost_auth_token_refreshandsubmit_feedbackcontrol subtypes
The v0.3.168 catchup added:
- new system message subtypes (
ThinkingTokensMessage,CommandsChangedMessage), theMessageDisplayhook event, and theMessageOriginKindAutoContinuationorigin variant ResultMessagetiming fields (Duration*MS), anOverloadedSDKAssistantMessage.Errorvariant, and theSessionTitlefield onSessionStart+UserPromptSubmithook inputs- hook output additions:
ReloadSkillsonSessionStartandAdditionalContextonNotification/PostToolBatch/PostToolUse*/PreToolUse/SessionStart/Setup/Stop/SubagentStart/SubagentStop/UserPromptExpansion/UserPromptSubmit - new control RPCs:
OnUserDialogcallback forrequest_user_dialog,Stream.ReloadSkills, andStream.RegisterRepoRootwithWithReloadClaudeMD/WithReloadPlugins/WithReloadSkills - managed-settings batch:
RequiredMinimumVersion,RequiredMaximumVersion,SwitchModelsOnFlag,ForceLoginMethod"gateway",Settings.FallbackModellist,PluginSuggestionMarketplaces, marketplaceSkipLfs, plusWorkflows/EnableWorkflows/WorkflowKeywordTriggerEnabled/Ultracodeknobs - correctness fixes:
pending_permission_requestsround-trips on control success payloads, marketplace source variants documentskipLfsflow, andEffort/ strict-MCP docstrings refreshed for clarity
PRs in this cycle (squash-merged): #96 MessageDisplay hook, #97 ThinkingTokensMessage, #98 SessionStart reloadSkills, #99 SessionTitle hook fields, #100 workflows + ultracode + pluginSuggestionMarketplaces, #101 marketplace skipLfs, #102 option docstring refresh, #103 pending_permission_requests round-trip, #104 CommandsChangedMessage, #105 Stop/SubagentStop additionalContext, #106 OnUserDialog + request_user_dialog, #107 reload_skills RPC, #108 register_repo_root + Stream.RegisterRepoRoot, #109 overloaded assistant error, #110 MessageOriginKindAutoContinuation, #111 ResultMessage timing fields, #112 Settings.FallbackModel list, #113 managed required/maximum version + switchModelsOnFlag + forceLoginMethod "gateway".
The v0.3.177 catchup added:
- the
ModelRefusalFallbackMessagesystem subtype (model_refusal_fallback) andAssistantMessage.Supersedes— the refusal-fallback supersede/retract signals emitted when a refused turn is retried on a fallback model - the experimental
get_usagecontrol request (Stream.GetUsageExperimental) returning session cost/usage totals, claude.ai plan rate-limit windows, and local-transcript behavioral attribution Options.SupportedDialogKinds(+WithSupportedDialogKinds) plumbed into the initialize request, rejected at init withoutOnUserDialogpending_user_dialog_requestson control responses,MCPServerToolPolicy.OrgMaxPermission,PluginConfig.SkipMcpDiscovery, andRateLimitInfo.OverageInUse- managed Settings parity:
EnforceAvailableModels,DisableBundledSkills,DisableArtifact,FooterLinksRegexes,WheelScrollAccelerationEnabled
PRs in this cycle (squash-merged): #116 model_refusal_fallback + supersedes, #117 get_usage control request, #118 SupportedDialogKinds, #119 pending_user_dialog_requests, #120 OrgMaxPermission, #121 SkipMcpDiscovery, #122 OverageInUse, #123 Settings parity.
The v0.3.185 catchup added:
- two new system message subtypes:
WorkerShuttingDownMessage(worker_shutting_down, a live-tail-only graceful-teardown signal) andInformationalMessage(informational, a leveled text banner for hook feedback / slash-command output) thinking_displayonset_max_thinking_tokens:SetMaxThinkingTokensnow takes optionalWithThinkingDisplay/WithThinkingDisplayAPIDefault, modeling the omit / value / explicit-null wire states- the optional
ExitWaitertransport capability (WaitForExit), implemented bySubprocessTransportand kept off the coreTransportinterface - new fields:
RateLimitInfocredit-exhaustion (ErrorCode,CanUserPurchaseCredits,HasChargeableSavedPaymentMethod),ResultMessage.TimeOriginMs,UserMessage.SenderTaskID - Settings parity:
Settings.DisableClaudeAiConnectors,SettingsAttribution.SessionURL,SettingsSandbox.AllowAppleEvents
PRs in this cycle (squash-merged): #126 worker_shutting_down, #129 informational, #130 thinking_display, #131 RateLimitInfo credit fields, #132 ResultMessage time_origin_ms, #133 UserMessage senderTaskId, #127 Settings parity, #128 transport WaitForExit, #134 docs refresh.
The v0.3.195 catchup added:
Stream.SetMcpPermissionModeOverride— theset_mcp_permission_mode_overridecontrol request (deferred in v0.3.185 for want of a wire shape) now ships upstream. Pins or clears a tighten-only per-MCP-server permission-mode override; themodeargument is a tristate (default/auto/ nil-clears, the last serialized as explicit JSON null) and the result carries an optional typo-detectionwarningStream.Reinitialize— re-sends theinitializecontrol request to a running CLI (past the cached first-connect result), so a client reattaching after a transport gap has blockedcan_use_tool/request_user_dialogrequests redelivered- the
ModelRefusalNoFallbackMessagesystem subtype (model_refusal_no_fallback), emitted when a turn refuses with no fallback model configured, plusRefusedUserMessageUUIDonModelRefusalFallbackMessage(the rewind/edit-and-retry target) SettingsSandbox.Credentials— credential-file / env-vardenyprotection for sandboxed commands- Settings parity:
RespondToBashCommands,DisableSideloadFlags, andteammateMode: iterm2 - additive enum/usage parity:
RateLimitTypeseven_day_overage_included,TerminalReasonbackground_requested, andUsageRateLimits.ModelScoped(per-model weekly windows on theget_usageresponse)
PRs in this cycle (squash-merged): #136 SetMcpPermissionModeOverride, #138 Reinitialize, #139 model_refusal_no_fallback, #141 RefusedUserMessageUUID, #137 sandbox credentials, #140 Settings parity, #142 usage/enum parity, #143 docs refresh.
The v0.3.201 catchup added:
AgentDefinition.Observer/ObserverMessage— an agent type auto-spawned as a read-only background observer whenever the agent runs, plus a supplemental postamble appended to each activity digest sent to it- two
MessageOriginprovenance kinds:observer(a message injected by a background observer agent, carryingSenderTaskID) andobserver-activity(an activity digest routed to the observer) BaseHookInput.PromptID— theprompt_idUUID correlating a user prompt with all subsequent events (also emitted as the OpenTelemetryprompt.idattribute), so hook output can be joined to OTel events at prompt grainModelInfo.ResolvedModel— the canonical wire model id an alias catalog row resolves to (e.g.sonnet→claude-sonnet-5)- sandbox credential
maskmode:SettingsSandboxCredentialEnvVargainsmask(sentinel-in-sandbox, real value injected at the proxy) alongsidedeny, plusInjectHoststo narrow substitution andSettingsSandboxCredentials.AllowPlaintextInjectfor the plain-HTTP path PermissionContext.RequiresUserInteraction— parsed fromrequires_user_interactionon the inbound permission request; true when the tool's approval card is itself the interaction surface (hosts must not offer a one-tap allow/deny)- Settings parity:
EnableArtifactandAskUserQuestionTimeout(60s/5m/10m/never)
PRs in this cycle (squash-merged): #148 AgentDefinition observer fields, #146 observer message-origin kinds, #149 BaseHookInput prompt_id, #147 ModelInfo resolvedModel, #150 sandbox credential mask mode, #151 permission requires_user_interaction, #152 Settings parity, #153 docs refresh.
Deferred this cycle (no observable wire shape or a non-mechanical port):
- Out-of-band permission responses — the TS
CanUseToolgained aPermissionResult | nullreturn plus arequestIdand theTransport.expectControlResponse/Query.awaitControlResponsepair, letting a consumer answer a permission request out-of-band and have the SDK skip its own transport write. Go'sCanUseToolreturns aPermissionResultwith no cleannullanalogue, and the feature needs a transport-interface contract change; deferred to a dedicated design pass rather than forced into this cycle. - Type-only control-request union members (
list_models,get_workspace_diff,get_plan,set_cwd) and message union members (control_request_progress,conversation_reset,active_goal) — added to the upstream unions but their struct bodies are unexported and absent from the shipped runtime, so there is nothing to model yet. Deferred until the wire shape is observable. - The
sdk-tools.d.tstool-schema surface (ReportFindings,ClaudeDesign,Projectspresent_to_user, and friends) — the Go SDK models a curated subset of common tool inputs, not the full tool-schema catalog.
The v0.3.207 catchup added:
ActiveGoalMessage— the top-levelactive_goalmessage reporting the/goalStop hook state;Valueis nil when the goal is cleared, otherwise carries the condition plus iteration/reason bookkeeping. Concretized upstream from the type-only member deferred in v0.3.201.ConversationResetMessage— the top-levelconversation_resetmessage emitted by/clear, plan-mode exit, and fresh-session flows, carrying theNewConversationIDto mount a fresh transcript under. Also concretized from the v0.3.201 type-only member.ControlRequestProgressMessage— thesystem/control_request_progressmessage for a long-running client-originated control request, correlated byRequestID; retry counters are pointers, populated only for theapi_retrystatus. Concretized from the v0.3.201 type-only member.BackgroundTasksChangedMessage— thesystem/background_tasks_changedmessage carrying the full live background-task set on every membership change (REPLACE semantics, a level signal rather than a task_started/notification edge, reset per CLI process)SystemMessage.Capabilities— the optionalcapabilitiesstring array onsystem/initfor feature-detecting protocol behavior instead of version-sniffing (open set;interrupt_receipt_v1is the first value)- interrupt receipt —
Stream.InterruptWithReceiptreturns theInterruptReceipt.StillQueueduuids of async user messages that survive the interrupt, on CLIs advertisinginterrupt_receipt_v1;Stream.Interruptstays as the fire-and-forget wrapper (no breaking signature change) - six new
TerminalReasonvalues:api_error,malformed_tool_use_exhausted,budget_exhausted,structured_output_retry_exhausted,tool_deferred_unavailable,turn_setup_failed MessageOrigin.Body— the decoded peer-envelope body (byte-exact with what the model sees), set only for thepeerorigin kindSessionMessage.ParentAgentID— the agentId of the subagent that spawned a subagent transcript message, nil for depth-1 subagents, the main session, or older metadata
PRs in this cycle (squash-merged): #155 active_goal, #156 background_tasks_changed, #157 control_request_progress, #158 conversation_reset, #159 init capabilities, #160 interrupt receipt, #161 TerminalReason values, #162 MessageOrigin peer body, #163 SessionMessage parent_agent_id.
Deferred this cycle (Go models no matching surface):
mcp_callstaging (input_files/output_files/expires_at/timeout_mson the mcp_call control request) — the Go SDK implements no outboundmcp_callcontrol request.- Tool input/output schema churn:
ScheduleWakeup'sstop/optional fields,Artifact'saction/favicon/list mode,EnterWorktree,ExitPlanModepermissions deprecation, and theArtifactOutputtype — none of these tools are in the curated tool-input subset. - Assistant-message
usageAPI sub-fields (citations, widenedservice_tier,inference_geo,speed,iterations) — the GoUsagestruct is a simplified projection; the raw API usage is opaque.
Not ported earlier cycles: listSessions' includeProgrammatic filter — a
local-filesystem session-picker option whose sibling includeWorktrees the Go
ListSessions never mirrored, and SDKSessionInfo carries no entrypoint field
to filter on. The rewind_conversation and add_directory control-request
union members ship without an exported wire body or a public Query method, so
there is nothing to model yet — both deferred until upstream exposes them.
The v0.3.215 catchup added:
APIProviderAnthropicGoogleCloud— theanthropicGoogleCloudvalue on theapiProvideraccount enum (the Anthropic-managed Google Cloud backend).AssistantMessagewrapper fieldsResumedFromIncompleteThinking(this turn continued a truncated signed-thinking block; must survive a bridge replay),Aborted(truncated by an interrupt beforestop_reason), andTimestamp(per-message ISO completion time, display-only — not an ordering key).PluginInfo.Version— the plugin'splugin.jsonmanifest version, on both thereload_pluginsresponse and theinitializesystem message (plugin-author-controlled; validate before trusting).- Permission-request
PermissionContext.SuppressAlwaysAllowRule(omit any persistent "don't ask again" affordance) andMatchedAskRule(a user ask rule forced the prompt while it still carries the tool's own decision reason; render-unsafe, sanitize before display). MessageOrigin.Subkind—scheduled-triggeron thetask-notificationorigin kind, marking a delivery that is the fired stored prompt of a scheduled task/routine.ToolProgressMessagefieldsHeartbeat,SubagentType, andSubagentRetry({agent_id, attempt, max_retries, retry_delay_ms, error_status, error_category};error_statusis nil for connection errors).UserPromptSubmitInput.Source(who authored/injected the prompt; trial-gated to Anthropic-internal sessions) and theforkvalue on theSessionStarthook source.SettingsfieldsProcessWrapper(corporate launcher argv prefix),FeedbackDrafts(notify/quiet/offfor the SendFeedback tool), andVimInsertModeRemaps(vim INSERT-mode two-char remaps to<Esc>).
PRs in this cycle (squash-merged): #168 anthropicGoogleCloud, #169 assistant wrapper fields, #170 PluginInfo version, #171 permission ask-rule fields, #172 MessageOrigin scheduled-trigger, #174 tool_progress subagent fields, #173 hook input source, #175 settings parity, plus this docs refresh.
Deferred this cycle (Go models no matching surface):
- New tool schemas
RefreshMcpTools,SendFeedback, andProposeSkills, plus tool input/output field churn (Artifactscope/title,BashtimedOutAfterMs/backgroundCwdHint,TaskmodelsUsed, and friends) — none are in the curated tool-input subset. - The alpha usage/policy prefix constants (
ORG_POLICY_LIMIT_PREFIXES,USAGE_LIMIT_ERROR_PREFIXES,USAGE_TRANSITION_PREFIXES,USAGE_WARNING_PREFIXES) — runtime string arrays with no wire type; the GoUsagestruct is a simplified projection. signalCodeon the process-exit transport type — a NodeChildProcessdetail; the Go transport models exit differently.set_max_thinking_tokens'max_thinking_tokensbecoming optional and theeffortLevel: 'max'session-scoped value — already representable in Go (SetMaxThinkingTokenstakes a nil-able*int;EffortLevelis a string alias), so no struct change.- Upstream doc-only text (docs.anthropic.com → platform.claude.com /
code.claude.com link migrations, the
setMcpServersplugin-exemption note, and themcp_callon-demand server bring-up note).
The v0.3.220 catchup added:
FastModeDisabledReason— the enum explaining why fast mode declined a turn (free,preference,extra_usage_disabled,network_error,unknown,not_first_party,disabled_by_env,model_not_allowed,sdk_opt_in_required,pending), added everywherefast_mode_statealready lives:SDKControlInitializeResponse,ResultMessage,SystemMessage.ModelInfopricing fieldsCanonicalModel(the canonical id used for the pricing lookup, e.g.claude-opus-4-7) andProvider(the API provider that served the model).MessageOriginpeer fieldsFromSession(the sender's host-openable session id, a UI navigation target) andVerifiedPeerPid(the kernel-verified pid of the connecting process — the field to key sender identity on, not the forgeableFrom).ResultMessagesuccess fieldsUserMessageUUID(uuid of the user message that drove the turn) andRequestSentWallMs(wall-clock send time, ms).RewindFilesResult.SkippedLinks— tracked files not restored/deleted because a symlink, hard link, or other non-regular file was detected (real rewinds only).- The
DirectoryAddedhook event andDirectoryAddedInput(Directory,Source=slash_command/register_repo_root), fired when a directory is registered as a working-directory root. Stream.InterruptCancelQueuedplusInterruptReceipt.Cancelledand theinterrupt_cancel_queued_v1capability — an interrupt that also cancels queued/pending-dispatch commands rather than leaving them to run.- Sandbox settings
SettingsSandboxNetwork.StrictAllowlist(deterministically deny hosts not inallowedDomains) andSettingsSandboxFilesystem.Disabled(skip filesystem isolation while keeping network + seccomp isolation). SettingsfieldsWorkflowSizeGuideline(advisory dynamic-workflow size),EmojiCompletionEnabled(:emoji:typeahead),PrecomputeCompactionEnabled(precompute the compaction summary in the background), andVoiceEnabled(hold-to-talk dictation).
PRs in this cycle (squash-merged): #179 fast_mode_disabled_reason, #180 ModelInfo pricing fields, #181 peer origin provenance, #182 result timing fields, #183 rewind skippedLinks, #184 DirectoryAdded hook, #185 interrupt cancel_queued, #186 sandbox strictAllowlist/disabled, #187 settings parity, plus this docs refresh.
Deferred in that cycle (Go models no matching surface):
sdk-tools.d.tschurn (e.g.liveSubscriptionon a tool output) — not in the curated tool-input subset, the same standing deferral as prior cycles.- The
"bubble"permission mode removed from onesdk-toolsmode enum — the Go SDK never modeledbubble, so no change. set_model'smodelbecoming nullable (null/'default'resets to the session default) — already representable:SetModelsends"default"(or omits the field).bridge.d.tsbrowser-bridge callbacks (onRenameSession/therename_sessionrequest,onPermissionResponsereturning a boolean) — TS-internal browser bridge, not the wire/control protocol the Go SDK models.- Upstream doc-only comment rewordings (supportedCommands push tracking, skills canonical-name matching, add_directory strict-subdirectory + duplicate-denied, mtime "integer milliseconds" phrasing, AgentToolCompletedOutput, force- overwrite upsert).
The v0.3.226 catchup added:
SessionOptions.ResumeDropsTurn+WithResumeDropsTurn— the fork-point guard for a truncatingResumeSessionAtresume. It names the prompt UUID of the turn being discarded, and the CLI refuses the resume when anything else (a queued user message, a task notification absorbed mid-turn) falls in the discarded range. The refusal is a deterministicerror_during_executionresult prefixedResume rejected by --resume-drops-turn:— route it to rewind-recovery, not a retry.MessageOriginKindUnclassifiedandMessageOriginSubkindPeerSendMessage— the latter marks a coordinator co-memberSendMessagedelivery, which carries task-notification prompt authority but stays distinguishable so the newCrossSessionInboundsetting can apply to it.ModelRefusalFallbackMessage.Scope(session/local) — whether the fallback swapped the session model or only served one subagent, side-question, or background-fork response. Absent from older CLIs; empty meanssession.- Structured sandbox credential masking:
maskmode oncredentials.filesplusExtract,OnExtractNoMatch,Decode: "jwt",MaskClaimson files and env vars,MaskDuplicatesandInjectHostson files. - Sandbox credential SigV4 re-signing:
AWSPairs(explicit key-id/secret/session groupings for non-standard variable names) andSigV4policies (denyby default, orpassthrough) for the streaming, presigned, and SigV4A shapes the proxy cannot re-sign. SettingsfieldsDialogExpiry(how long a remote-forwarded dialog or a HELD cross-session message stays parked before resolving to its safe default) andCrossSessionInbound(accept/hold/refusefor inbound peerSendMessage; unset means permission-mode-class parity, not accept).SettingsMarketplaceSourceArchive— zip-archive plugin sources (url, optionalsha256that pins the download and doubles as version identity), plus theerrorstring onunsupportedsources and theowner/*wildcard that github entries may use in the managed-settings policy lists and nowhere else.
PRs in this cycle (squash-merged): #190 resumeDropsTurn, #191 origin unclassified/peer-send-message, #192 refusal fallback scope, #193 credential masking, #194 credential SigV4, #195 settings parity, #196 archive marketplace source, plus this docs refresh.
Deferred this cycle (Go models no matching surface, or needs a design call):
OnElicitation/OnUserDialogout-of-band responses — both callbacks now receive the control envelope'srequestIdand may returnnullto suppress the SDK's owncontrol_responsebecause the consumer already answered the request out of band. Same transport-contract family as thecanUseToolnull path deferred in v0.3.201, andrequestIdon its own is useless without the suppression channel, so this needs an API design call (sentinel error vs. a third return value) rather than a mechanical port.bridge.d.tsfailure classification (CredentialsRejection/isCredentialsRejection,CreateSessionFailure.reason, therequest_rejectedandmalformed_responseterminal reasons) — browser bridge, not the wire/control protocol the Go SDK models.sdk-tools.d.tschurn (Feedbackgainingfailure_mode/task_category,RemoteTriggergainingcreate_webhook_trigger) — neither tool is in the curatedtool_inputs.gosubset, the standing deferral.- Zod-internal schema plumbing (
ZodPipe<ZodTransform<…>>wrappers on the sandbox credential arrays, widenedbwrapPath/socatPathtransform input) — TS-side validation machinery with no wire change. - Upstream reformatting:
import * as zand the single-quote rewrite ofEXIT_REASONS,HOOK_EVENTS,ORG_POLICY_LIMIT_PREFIXES, and theUSAGE_*prefix arrays. No value changed.
One pre-existing divergence surfaced while diffing this release and is left as
follow-up work: with no OnUserDialog registered, the Go SDK answers a
request_user_dialog with cancelled, while the TS SDK has stayed silent since
at least v0.3.220 so a renderer-bearing client or the worker's park deadline can
settle it. v0.3.226 only reworded the doc comment to match the behavior it
already had.
The v0.3.233 catchup added:
AssistantMessage.ContextUsage— the structured twin of the/contextreport, riding as a wrapper-level sibling on the synthetic assistant message that delivers the markdown table, so it is never replayed to the model. This is a separate wire shape fromSDKControlGetContextUsageResponse, which mirrors the camelCase/contextcontrol response: the new one is snake_case, deliberately smaller, and upstream commits to evolving it additively. Category rows and the over-limit reason get named kinds (ContextUsageUsed/Free/Buffer/Deferred,ContextWindowHardLimit/Compaction); classify rows on the kind, never on the display name.SystemMessage.TerminalSlashCommands— the subset ofSlashCommandswhose UX is bound to the local terminal (exit,statusline). Remote UIs should hide these; empty means either an older CLI or an untagged session, and showing everything is the right fallback for both.SettingsMarketplaceSourceCommand— plugin sources whose directory is produced by running a marketplace-declared command that prints one absolute path (command, optionaltimeout,mode: "copy"/"link"). Underlinkthe cache points at that directory in place, so it must stay valid for the life of the process and a changed path is the only signal of new content. Gated by the managed-settings-onlyDisableCommandPluginSources, which followsAllowManagedHooksOnlywhen unset.Settings.AdditionalMarketplaces/AllowedMarketplaces— aliases read exactly asExtraKnownMarketplaces/StrictKnownMarketplaces. Prefer the canonical spellings while older clients share the same settings file: they ignore the alias outright, and an allowlist that silently isn't there means unrestricted. The SDK does not fold aliases into the canonical fields, since resolving that conflict is the CLI's job.Settings.ForceLoginGatewayURL— the Cloud gateway URL to pre-fill and auto-connect to alongsideForceLoginMethod: "gateway". Admin-controlled managed settings only.queued_notificationsonSystemMessage.Capabilities— the CLI accepts inboundqueued_notificationstream messages and drains them viaReadNotifications. Doc-only here:Capabilitiesis already an open[]string, and the drain tool sits outside the curatedtool_inputs.gosubset.
PRs in that cycle (squash-merged): #200 assistant context_usage, #201
terminal_slash_commands, #202 command plugin source, #203 marketplace settings
aliases, #204 forceLoginGatewayUrl, plus its docs refresh.
Deferred in v0.3.233:
sdk-tools.d.tschurn (ReadNotificationsandProposeGoaltools,RemoteTriggergaininglist_runs/get_run_log,BashgainingbackgroundEndsWithFinalResponse, the commitbranchfield,output_tokens_details.thinking_tokens) — outside the curatedtool_inputs.gosubset, the standing deferral.bridge.d.tsclose codes 4093 (presence heartbeats failing while SSE stays healthy) and 4094 (worker credential expired on the request path), plus theisCreateSessionFailurenarrowing — browser bridge, no Go surface.- The
attributionindex signature — Go already ignores unknown keys on decode, so an extras map would only add round-trip ambiguity to the public API. - A
ripgrepsettings doc reword: it is honored only from user, managed/policy, or CLI (--settings) settings, and ignored from project settings. Behavior the Go SDK does not model either way.
The v0.3.241 catchup added:
SystemMessage.Effort— the effort level the session will send on its next request, after env overrides, session state, org caps and model-support downgrades. A genuine tri-state, so it is aNullableEffortLevelvalue taggedomitzerorather than a pointer: an explicitnullmeans no effort parameter will be sent at all, while an absent key means the host does not publish the field. A pointer cannot carry that distinction, becauseencoding/jsonresolves anullinto a nil pointer without ever callingUnmarshalJSON.TaskStartedMessage.IsBackgrounded/SpawnDepth— whether a task was registered in the background or in the foreground with the spawning tool call blocking on it, and the nesting depth of a spawned subagent.task_startedreports registration state only; a later move to the background arrives asTaskUpdatePatch.IsBackgrounded.MessageOrigin.FromMode— the sending session's permission class on a peer origin (PeerFromModeBypass/Prompting). An absent value means hold, not a default class: a recipient that runs without asking delivers same-class messages immediately and holds cross-class or undeclared senders.MessageOriginSubkindProjectsRelay— a Claude Code Projects delivery composed server-side for a project coordinator and addressed to one of its thread sessions. Framed as a coordinator message only when it carries the server's relay stamps; one without them keeps the generic background-notification frame.AssistantMessageErrorAccountOnHold, plus constants naming the four liveapiKeySourcevalues (ANTHROPIC_API_KEY,apiKeyHelper,/login managed key,none). The field stays a plainstring— it is an open set. Upstream's five legacy members get no constants: current CLIs never emit them.HookJSONOutput.ClassifierContext— host-asserted context shown to the auto-mode permission classifier alongside a PostToolUse result. Capped at 2000 UTF-16 code units shared across all hooks on one call, honored on synchronous responses only, and unused on calls the classifier transcript omits. Pair an assertion with the rewrite it describes in the same hook result, but never return an identity rewrite to achieve that pairing — hooks run in parallel on the original output and it would compete last-write-wins with a real redaction.SuppressOriginalPromptonUserPromptExpansion— the field is declared on the expansion hook's output too, with identical semantics. The response builder now names the hook that produced the envelope instead of hardcodingUserPromptSubmit.poll_eventonUserPromptSubmitInput.Source— doc-only; both hook-input builders already mapsource. It is the one source where a blocking verdict rejects something not yet delivered: the hook fires at enqueue time, before the event's delivery ack exists.SDKControlInitializeResponse.HooksApplied— whether a repeated initialize's hooks replaced the registered set, or were ignored because another client configured the session. Nil is not false: a CLI predating the field ignored hooks on every repeated initialize without saying so.Settings.SyncClaudeAiSkills,Worktree.Location,KeybindingFlavor,AutoContinueAtUsageLimit,ModelSettings,Spellcheck. Onlyfalseis honored forSyncClaudeAiSkills, so it is a*bool.Worktree.Locationis desktop-only — the CLI does not read it for--worktree,EnterWorktree, or agent isolation. A per-model persisted effort tops out atxhigh, where the init message's applied effort also admitsmax.SettingsMarketplaceSourceURL, plusheadersHelperdocumented on url sources andheaders/headersHelperon inline catalog entries. Both already ride the open source map. The two helpers differ in lifetime: a url source's is re-run on every marketplace refresh and runs from the Claude config home (so relative paths do not resolve where a caller expects), while a catalog entry's runs only on explicit install or update.
PRs in this cycle (squash-merged): #207 init effort, #208 task_started
backgrounding, #209 peer fromMode and projects-relay, #210 account_on_hold
and apiKeySource constants, #211 classifierContext, #212 prompt-hook parity,
#213 hooks_applied, #214 Settings parity, #215 marketplace headersHelper,
plus this docs refresh.
Deferred in v0.3.241:
sdk-tools.d.tschurn (the Artifact/read/watch tool input unions) — outside the curatedtool_inputs.gosubset, the standing deferral.bridge.d.ts: theonStopTaskcallback, and the terminal-reason split whererequest_rejectedgainsstatusand an originsource, andmalformed_responsebecomes its own variant. No Go bridge transport exists.ExitReason/EXIT_REASONSlostbypass_permissions_disabled, andSDKControlGetPlanRequest/SDKControlGetWorkspaceDiffRequestwere deleted outright (gone fromsdk.mjstoo). The Go SDK never modeled either surface, so both removals are no-ops here.
The v0.3.251 catchup added:
PreModelSwitch/PostModelSwitchhooks — the first new hook pair sinceDirectoryAdded, and the first hook input to carry prompt-cache economics. OnlyPreModelSwitchis vetoable, which is whysourcediffers between them: the post phase addsautoandresume, switches the CLI initiates itself and so never offers for approval.- Per-server
timeouton SDK MCP servers, in milliseconds. Values under 1000ms are ignored, and changing it on an already-registered server does nothing until the server is removed and re-added. ModelUsage.CostBasis(plusCanonicalModelandProvider, which predate it and were never modeled) — which price table the most recent request was priced at. Empty until this process has priced a request for the model, which a--resumemakes common; read that as list pricing.PerTaskStopAffordanceon the initialize request.UserMessageUUIDon every first reply frame, plusQueuedTurnCounton results so a host draining a send queue can tell "this run is done" from "more is inbound".- The
Ambientmarker on task frames, which covers everySkipTranscripttask and more. DefaultToNoon permission asks.- SessionStart resume-cost fields, so a host can price a resume before committing to it.
- Settings:
PromptCacheTTL,SubagentPromptCacheTTL,DesktopSessionCleanupPeriodDays,SyncClaudeAiPlugins,ManagedSourcesBehavior,SpinnerTipsOverride, plus the model picker and contracted-rate pricing.
PRs in this cycle (squash-merged): #218 model-switch hooks, #219 SDK MCP
per-server timeout, #220 pricing provenance, #221 perTaskStopAffordance,
#222 user_message_uuid, #223 queued-turn count, #224 ambient marker,
#225 default_to_no, #226 SessionStart resume pricing, #228 Settings parity,
#229 model picker and pricing.
The v0.3.263 catchup added:
- Plugin delivery over the initialize request.
pluginDelivery: 'initialize'moves the plugin list off the command line and into the initialize payload, because Windows refuses a command line past 32,767 characters and one--plugin-dirper plugin blows through it.'argv'remains the Go default:initializeneeds Claude Code 2.1.261+, and an older binary exits at startup on the unknown--await-initializeoption rather than degrading. PermissionPrompts— lets a session declare it has no approval surface.- System prompt
snapshot, a genuine tri-state: omitted records a bare preset,falsenever records. Plus sending a preset system prompt'sappend. update_settingsandreload_output_stylescontrol requests — the first new control subtypes sinceget_settings— and options onget_context_usageandget_usage.SDKMcpResourceLinkandResourceLinkson task notifications. A backgrounded MCP task'stool_resultis placeholder text and its real result arrives as the notification, so this is the only place a host learns which files the call produced.UserMessageUUIDson reply, result and thinking frames.UserMessageUUIDalone turned out to be insufficient: when the host merges several close-together sends into one turn it names only the last member, leaving every other sender unable to recognize the reply. The result list can be strictly longer than the reply list, because queued messages fold into a running turn between tool rounds.- The first-frame result timings —
FirstContentFrameMs,FirstStreamPostMs,FirstStreamPostAckMs,FirstStreamPostWallMs. Success results only. NoResponseonapi_retry. Its presence, not its durations, is the point: for a first-byte timeoutMaxRetriesis that cause's own cap — normally a single retry — rather than the session's retry budget.ModelUsage.ThinkingTokens, already counted insideOutputTokensso summing the two double-counts. It tallies only turns run on a CLI recording the field, which makes it partial for a session resumed across versions and unsafe as a denominator.- Settings:
Permissions.BlockReadsOutsideWorkingDirectories(nested in the permissions block),ManagedMCPServers,BashOutputMaxChars,TaskOutputMaxChars,TimeFormatandTimeZone.TimeFormatis an open string type rather than an enum because upstream unions the four presets with any strftime pattern containing%.
PRs in this cycle (squash-merged): #231/#232 plugin delivery, #233
permissionPrompts, #234/#235 system prompt append and snapshot, #236
update_settings, #237 reload_output_styles, #238 control-request options,
#239 resource_links, #240 user_message_uuids, #241 first-frame timings,
#242 no_response, #243 thinkingTokens, #244 Settings parity, plus this docs
refresh.
Deferred in v0.3.251 and v0.3.263:
bridge.d.tsonSetModel(noticesand aPromisereturn) — no Go bridge transport exists. Deferred every cycle since v0.3.233.sdk-tools.d.tsschema churn — the standing deferral since v0.3.207.- The
spinnerTipsOverride.tipsunion element — breaking, outbound-only, and with no decode consumer to justify it. - The prompt word-editing keys setting, which upstream now documents as having no effect. The Go field stays: removing it is a breaking change for no gain.
Some areas remain intentionally limited by the CLI or integration harness: desktop/IDE-only settings are not modeled exhaustively, several runtime control paths have unit coverage plus skipped integration slots until stable live CLI fixtures exist, and alpha task/agent behavior should still be checked against the installed Claude Code CLI version.
The v0.3.278 catchup added:
- MCP server provenance.
McpServerProvenance(name,source) rides the permission request, the tool-related hook inputs, and themcp_statusand init reporting rows. Trust keys onsource, never on the name or themcp__*tool-name prefix:source: "sdk"is the only value that means an in-process server the host itself registered, and a configured server of the same name can never claim it.IsSDK()on the status rows is the decode-side check. ProjectConfigRootoption and--project-config-rootflag — the trusted checkout a worktree is of. Project settings,.mcp.json, the.claudeconfig trees andCLAUDE_PROJECT_DIRcome from here rather thancwd, so what the branch checked out incwdis not what the session runs.highlightsthinking display mode — one short title per stretch of thinking. The API honors it only for Anthropic-hosted Claude Code sessions; any other client's request still succeeds but the session falls back toomittedonce the API rejects the value.userSettingssource onupdate_settings— takeseffortLevelonly, saved as the default for the session's current model as/effortsaves it, without changing the running session's level.StartupFailureReasonandSDKStartupFailureReasonon the result frame — a machine-readable cause a stream-json run writes before exiting on a known startup failure, so a host can offer the fix instead of a blind retry.UsageReportandSDKUsageReporton the assistant message that delivers a/usageresult — the session totals, the plan's usage rows as the server sent them, and extra-usage spend, for a client that renders a card from data rather than the text.- Four first-post timing fields on the result frame:
FirstStreamPostQueueWaitMs,FirstStreamPostQueuedBehind,FirstTextPostMs,FirstTextPostWallMs. Stream.RenameSession— therename_sessioncontrol request, with a source (remote, the CLI default, orhost) and a session-id guard that refuses the rename if this process has since moved to another conversation.- Four one-field adds:
AgentDefinition.OmitClaudeMd(run a subagent without the user/project/local CLAUDE.md files, managed policy kept),SlashCommand.Builtin,HooksListingPolicy.PolicyUnreadable(managed settings present but unreadable, which failsedit_hookclosed), andTaskNotificationMessage.Reason(worker_restart). UserMessage.PastedContent— content pasted rather than typed, each entry theMessageParam['content']union modeled asPastedContentEntry(a string or a block array).- Settings: npm marketplace
version/registry,modelPricing.multiplierrange widened to(0, 10],taskOutputMaxCharsdeprecated to a no-op (the TaskOutput tool was removed),skipLfsdocumented as a no-op, andworkflowSizeGuideline"medium" now<10agents. - A behavior correction to
TotalCostUSD/ModelUsage: a resumed or forked session continues from the total its transcript saved rather than starting fresh, so the first result already carries the earlier turns.
PRs in this cycle (squash-merged): #260/#262/#263 MCP server provenance, #264
projectConfigRoot, #265 highlights, #266 userSettings, #267
startup_failure_reason, #268 usage_report, #269 first-post timings, #270
rename_session, #271 assorted field adds, #272 pasted_content, #273 Settings
parity, plus this docs refresh.
Deferred in v0.3.278:
set_chrome_browser_hints—sdk.mjsemits it viaQuery.setChromeBrowserHints, but it has no declaration anywhere insdk.d.ts(no request type, no method on the typedQuery), so there is nothing to model without guessing. Same posture asset_mcp_permission_mode_overridebefore v0.3.185 — wait for the wire shape.sdk-tools.d.tsschema churn — the standing deferral since v0.3.207.bridge.d.tsandbrowser-sdk.d.ts— no Go bridge/WebSocket transport exists.sdkMcpServerManifestson initialize — tracked in #256.
The v0.3.290 catchup added:
Options.VerbatimPromptsandUserMessage.ClientComposed. With the flag set, the CLI delivers prompt text as written: no@pathexpansion and no slash-command dispatch. Current CLIs also skip the turn-start attachment pass for such a turn, so nestedCLAUDE.md, skill listings and the like arrive after the first tool call instead of with the prompt.Stream.SendMessage— send a caller-builtUserMessage, the analogue of handing anSDKUserMessagetostreamInput. It's the only way to setPastedContent, the newInlinePastes, a per-messageClientComposed, a clientUUIDor aPriority.Stream.GetTaskOutput— theget_task_outputcontrol request: the last 8 KiB a background shell or Monitor task wrote, plus total size and a truncated flag, without a model turn.- MCP Apps (SEP-1865) support:
McpServerStatus.Toolswith each tool's validated_meta(UIResourceURI()resolves theui://resource it declares), andStream.ReadMcpResource, themcp_read_resourcecontrol request that fetches that resource from a server the CLI dialed. The reply is untrusted third-party HTML; render it sandboxed. The status rows also gained the long-missingerror,scopeanddisabledstate. conversation_resetgainstrigger(clear,plan_mode_exit,fresh_session,onboarding; open set), theuser_message_uuidof the/clearthat caused it, and a displaytimestamp.- Init gains
plugin_errors(load-time plugin failures, with the resolved path of a directory entry that didn't load at all),view_mode, and the plugin row's manifestversion. - Small decode-side adds: informational
tag, thesession-inboxtask-notification subkind and scheduled-triggerfireReason, theprovider_not_allowedstartup failure reason, and the first-text-post queue timings on the result. - Settings:
availableModelsMatch,deniedModels,allowClaudeInChromeWithManagedMcp,allowedProviders(an empty list allows no provider, so it is taggedomitzero),maxProseWidth,idleCompaction, the per-modelautoCompactWindow("auto"or a token count), andattribution: falseviaSettingsAttribution.HideAll. - A wire fix:
SetMaxThinkingTokens(ctx, nil)now sends an explicitmax_thinking_tokens: null. Current CLIs read an absent key as "leave the budget alone", so the old omitted encoding had quietly stopped resetting.
PRs in this cycle (squash-merged): #277 verbatimPrompts, #278
inline_pastes, #279 get_task_output, #280 mcp_status tools and _meta,
#281 mcp_read_resource, #282 conversation_reset trigger, #283
plugin_errors, #284 assorted field adds, #286 Settings parity, #287
attribution boolean, #288 the thinking-tokens null, #290
Stream.SendMessage, plus this docs refresh. #285 fixed a register-after-write
race in Initialize found along the way.
Deferred in v0.3.290:
prewarm()/SpareProcess/ClaimOptions(alpha). It rides--await-claimplus aclaim_sessioncontrol request whose shape is not declared insdk.d.ts, and its claim-refused / fall-back-to-query()lifecycle is a design call for the Go API rather than a mechanical port.sdkMcpServerManifestsOriginandsdk_mcp_manifests_parked— only meaningful alongsidesdkMcpServerManifests, tracked in #256.- Control subtypes
sdk.mjsemits with nosdk.d.tsdeclaration (export_conversation,get_status,get_plan, the Chrome and dialog getters,set_prompt_suggestions_paused), and the./coreJS entrypoint. sdk-tools.d.ts/bridge.d.tschurn — the standing deferrals.
The v0.3.296 catchup added:
- Per-run task identity:
RunIDon everytask_*message, on eachBackgroundTasklevel entry and on a background task's saved notification (MessageOrigin.RunID). A resumed task keeps itsTaskIDand opens a new run, soRunIDis how a host drops a late frame from an earlier run. One task's run ids sort in open order; nothing else about the format is promised.ParentTaskIDonTaskStartedMessageandBackgroundTasknames the subagent task that launched this one, and entries gainSubagentType. AgentIDonAssistantMessageandUserMessage: thetask_idof the subagent that produced the frame, stable across resume. CLI 2.1.296 forwards a subagent's prompt (a user frame) but not its assistant turns by default.AgentDefinition.AutoCompactWindow, which can only lower the window a subagent inherits.SettingsHook.OnFailure:"block"makes a command or http hook that can't start, times out, exits non-0/2 or prints invalid JSON fail closed, as if it exited 2.SDKControlInitializeResponse.ClaudeCodeVersion, so a caller can gate features right afterConnect; theorg_config_required_unavailable(retryable) andorg_config_refused(not) startup failure reasons; andRateLimitInfo.OverageEnabled.- An argv change: scalar flags now go out as one
--flag=valueelement, assdk.mjsdoes, so a value starting with-(a system prompt like"- be terse", a dash-leading path) can't be read as the next option.--settings,--managed-settings,--mcp-configandExtraArgskeep the pair and join only for such a value.
PRs in this cycle (squash-merged): #295 task run_id/parent_task_id, #296
agent_id, #297 autoCompactWindow, #298 hook onFailure, #299 init
version and enum parity, #300 --flag=value argv, plus this docs refresh.
Deferred in v0.3.296:
- The
sandboxoption's new merge into inlinesettings(key-by-key, with restriction lists unioned). The GoWithSandboxhas never reached the CLI at all, and itsSandboxSettingstype doesn't match upstream; fixing it means changing a public signature. Tracked in #301. bridge.d.tsclaudeCodeVersion(bridge host side) andsdk-tools.d.tschurn — the standing deferrals.
Porting from the TypeScript SDK - Go-side differences
A short list of places the Go SDK consciously diverges from the TS shape; if you are translating TS code, watch for these:
Options.Envmerges, it does not replace. TSOptions.envREPLACES the subprocess environment entirely; the GoOptions.Envis overlaid on top ofos.Environ(). If you relied on the TS replace-semantics, clear inherited variables yourself before spawning. There is no opt-in flag for replace mode.- Cancellation is
context.Context, notAbortSignal. TS forwards a derivedAbortSignalthat fires only after a stdin-EOF + grace window. In Go, that grace window lives insideSubprocessTransport.Close(): the SDK closes stdin, waits up to 5 seconds for the CLI to exit on its own, and only then force-kills. Anything you hang off the samecontext.Context(HTTP cancellation, in-flight tool teardown) inherits the same ordering guarantee. 'Skill'inAllowedToolsis deprecated upstream. Per TS SDK v0.3.150, passing'Skill'inAllowedToolsorAgentDefinition.Toolsis deprecated. UseAgentDefinition.Skillsfor per-agent preload. The Go SDK does not currently expose the upstream top-level user-facingskillsoption; the existingOptions.Skillsmirrors the control-init system-prompt loading allowlist, which is a different surface.PermissionModedefaults to"default".DefaultOptions()setsPermissionModeDefault, so the Go SDK always passes--permission-mode defaultand keeps manual approvals throughCanUseTool. Since v0.3.290 a TS host that omitspermissionModegets whatever the CLI picks, which is the settings'permissions.defaultModeor elseautowhere auto mode is available. SetPermissionModeto""for the TS behavior.WithSandboxis not sent to the CLI (#301).Options.Sandboxis never read, so it enables nothing. Until that's fixed, put the block inWithSettings(Settings{Sandbox: &SettingsSandbox{...}}), which is sent as--settings. Note that TS setsfailIfUnavailable: truewhen the option enables the sandbox; set it yourself to get the same fail-closed behavior.Effort"max"/"xhigh"are model-gated.EffortMaxrequires Opus 4.6/4.7 or Sonnet 4.6;EffortXHighrequires Opus 4.7. On unsupported models the CLI silently downgrades per its own policy. The CLI is the authority on which model accepts which level - the SDK only forwards the enum value.OnUserDialoganswers cancelled by default, including on unknowndialogKind. Per TS SDK v0.3.168,dialogKindis an open string union: a new CLI release can ship a kind the host has never seen. The contract is that the host MUST respond cancelled in that case, and the CLI then applies the dialog's default behavior. The Go SDK enforces this automatically: ifOnUserDialogis unset, or the callback returns a non-nil error, the SDK emits cancelled on the host's behalf. Callbacks that do recognize the kind should still returnUserDialogBehaviorCancelled(rather than fabricating a result) for any branch they cannot honor — a misbehaving host should never wedge the CLI on an unresolved dialog. As of TS SDK v0.3.177, declare the kinds the host can actually render viaOptions.SupportedDialogKinds: the CLI fails closed and never emits an undeclared kind, so the flow behind it degrades to its no-dialog behavior instead.
For internal architecture, see DESIGN.md. For CLI protocol details (how this and the official Typescript SDK actually work), see cli-protocol.md.
Testing
go test ./...
License
README fetched from the repository 4 days ago. The original is authoritative.