mirror of
https://github.com/sanbuphy/claude-code-source-code.git
synced 2026-04-04 12:04:56 +08:00
v2.1.88 反编译源码
从 npm 包 @anthropic-ai/claude-code 2.1.88 版本提取的反编译源码 包含 src/ 目录下的 TypeScript 源文件及 vendor/ 原生模块源码 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
443
src/entrypoints/agentSdkTypes.ts
Normal file
443
src/entrypoints/agentSdkTypes.ts
Normal file
@@ -0,0 +1,443 @@
|
||||
/**
|
||||
* Main entrypoint for Claude Code Agent SDK types.
|
||||
*
|
||||
* This file re-exports the public SDK API from:
|
||||
* - sdk/coreTypes.ts - Common serializable types (messages, configs)
|
||||
* - sdk/runtimeTypes.ts - Non-serializable types (callbacks, interfaces)
|
||||
*
|
||||
* SDK builders who need control protocol types should import from
|
||||
* sdk/controlTypes.ts directly.
|
||||
*/
|
||||
|
||||
import type {
|
||||
CallToolResult,
|
||||
ToolAnnotations,
|
||||
} from '@modelcontextprotocol/sdk/types.js'
|
||||
|
||||
// Control protocol types for SDK builders (bridge subpath consumers)
|
||||
/** @alpha */
|
||||
export type {
|
||||
SDKControlRequest,
|
||||
SDKControlResponse,
|
||||
} from './sdk/controlTypes.js'
|
||||
// Re-export core types (common serializable types)
|
||||
export * from './sdk/coreTypes.js'
|
||||
// Re-export runtime types (callbacks, interfaces with methods)
|
||||
export * from './sdk/runtimeTypes.js'
|
||||
|
||||
// Re-export settings types (generated from settings JSON schema)
|
||||
export type { Settings } from './sdk/settingsTypes.generated.js'
|
||||
// Re-export tool types (all marked @internal until SDK API stabilizes)
|
||||
export * from './sdk/toolTypes.js'
|
||||
|
||||
// ============================================================================
|
||||
// Functions
|
||||
// ============================================================================
|
||||
|
||||
import type {
|
||||
SDKMessage,
|
||||
SDKResultMessage,
|
||||
SDKSessionInfo,
|
||||
SDKUserMessage,
|
||||
} from './sdk/coreTypes.js'
|
||||
// Import types needed for function signatures
|
||||
import type {
|
||||
AnyZodRawShape,
|
||||
ForkSessionOptions,
|
||||
ForkSessionResult,
|
||||
GetSessionInfoOptions,
|
||||
GetSessionMessagesOptions,
|
||||
InferShape,
|
||||
InternalOptions,
|
||||
InternalQuery,
|
||||
ListSessionsOptions,
|
||||
McpSdkServerConfigWithInstance,
|
||||
Options,
|
||||
Query,
|
||||
SDKSession,
|
||||
SDKSessionOptions,
|
||||
SdkMcpToolDefinition,
|
||||
SessionMessage,
|
||||
SessionMutationOptions,
|
||||
} from './sdk/runtimeTypes.js'
|
||||
|
||||
export type {
|
||||
ListSessionsOptions,
|
||||
GetSessionInfoOptions,
|
||||
SessionMutationOptions,
|
||||
ForkSessionOptions,
|
||||
ForkSessionResult,
|
||||
SDKSessionInfo,
|
||||
}
|
||||
|
||||
export function tool<Schema extends AnyZodRawShape>(
|
||||
_name: string,
|
||||
_description: string,
|
||||
_inputSchema: Schema,
|
||||
_handler: (
|
||||
args: InferShape<Schema>,
|
||||
extra: unknown,
|
||||
) => Promise<CallToolResult>,
|
||||
_extras?: {
|
||||
annotations?: ToolAnnotations
|
||||
searchHint?: string
|
||||
alwaysLoad?: boolean
|
||||
},
|
||||
): SdkMcpToolDefinition<Schema> {
|
||||
throw new Error('not implemented')
|
||||
}
|
||||
|
||||
type CreateSdkMcpServerOptions = {
|
||||
name: string
|
||||
version?: string
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
tools?: Array<SdkMcpToolDefinition<any>>
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an MCP server instance that can be used with the SDK transport.
|
||||
* This allows SDK users to define custom tools that run in the same process.
|
||||
*
|
||||
* If your SDK MCP calls will run longer than 60s, override CLAUDE_CODE_STREAM_CLOSE_TIMEOUT
|
||||
*/
|
||||
export function createSdkMcpServer(
|
||||
_options: CreateSdkMcpServerOptions,
|
||||
): McpSdkServerConfigWithInstance {
|
||||
throw new Error('not implemented')
|
||||
}
|
||||
|
||||
export class AbortError extends Error {}
|
||||
|
||||
/** @internal */
|
||||
export function query(_params: {
|
||||
prompt: string | AsyncIterable<SDKUserMessage>
|
||||
options?: InternalOptions
|
||||
}): InternalQuery
|
||||
export function query(_params: {
|
||||
prompt: string | AsyncIterable<SDKUserMessage>
|
||||
options?: Options
|
||||
}): Query
|
||||
export function query(): Query {
|
||||
throw new Error('query is not implemented in the SDK')
|
||||
}
|
||||
|
||||
/**
|
||||
* V2 API - UNSTABLE
|
||||
* Create a persistent session for multi-turn conversations.
|
||||
* @alpha
|
||||
*/
|
||||
export function unstable_v2_createSession(
|
||||
_options: SDKSessionOptions,
|
||||
): SDKSession {
|
||||
throw new Error('unstable_v2_createSession is not implemented in the SDK')
|
||||
}
|
||||
|
||||
/**
|
||||
* V2 API - UNSTABLE
|
||||
* Resume an existing session by ID.
|
||||
* @alpha
|
||||
*/
|
||||
export function unstable_v2_resumeSession(
|
||||
_sessionId: string,
|
||||
_options: SDKSessionOptions,
|
||||
): SDKSession {
|
||||
throw new Error('unstable_v2_resumeSession is not implemented in the SDK')
|
||||
}
|
||||
|
||||
// @[MODEL LAUNCH]: Update the example model ID in this docstring.
|
||||
/**
|
||||
* V2 API - UNSTABLE
|
||||
* One-shot convenience function for single prompts.
|
||||
* @alpha
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* const result = await unstable_v2_prompt("What files are here?", {
|
||||
* model: 'claude-sonnet-4-6'
|
||||
* })
|
||||
* ```
|
||||
*/
|
||||
export async function unstable_v2_prompt(
|
||||
_message: string,
|
||||
_options: SDKSessionOptions,
|
||||
): Promise<SDKResultMessage> {
|
||||
throw new Error('unstable_v2_prompt is not implemented in the SDK')
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a session's conversation messages from its JSONL transcript file.
|
||||
*
|
||||
* Parses the transcript, builds the conversation chain via parentUuid links,
|
||||
* and returns user/assistant messages in chronological order. Set
|
||||
* `includeSystemMessages: true` in options to also include system messages.
|
||||
*
|
||||
* @param sessionId - UUID of the session to read
|
||||
* @param options - Optional dir, limit, offset, and includeSystemMessages
|
||||
* @returns Array of messages, or empty array if session not found
|
||||
*/
|
||||
export async function getSessionMessages(
|
||||
_sessionId: string,
|
||||
_options?: GetSessionMessagesOptions,
|
||||
): Promise<SessionMessage[]> {
|
||||
throw new Error('getSessionMessages is not implemented in the SDK')
|
||||
}
|
||||
|
||||
/**
|
||||
* List sessions with metadata.
|
||||
*
|
||||
* When `dir` is provided, returns sessions for that project directory
|
||||
* and its git worktrees. When omitted, returns sessions across all
|
||||
* projects.
|
||||
*
|
||||
* Use `limit` and `offset` for pagination.
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* // List sessions for a specific project
|
||||
* const sessions = await listSessions({ dir: '/path/to/project' })
|
||||
*
|
||||
* // Paginate
|
||||
* const page1 = await listSessions({ limit: 50 })
|
||||
* const page2 = await listSessions({ limit: 50, offset: 50 })
|
||||
* ```
|
||||
*/
|
||||
export async function listSessions(
|
||||
_options?: ListSessionsOptions,
|
||||
): Promise<SDKSessionInfo[]> {
|
||||
throw new Error('listSessions is not implemented in the SDK')
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads metadata for a single session by ID. Unlike `listSessions`, this only
|
||||
* reads the single session file rather than every session in the project.
|
||||
* Returns undefined if the session file is not found, is a sidechain session,
|
||||
* or has no extractable summary.
|
||||
*
|
||||
* @param sessionId - UUID of the session
|
||||
* @param options - `{ dir?: string }` project path; omit to search all project directories
|
||||
*/
|
||||
export async function getSessionInfo(
|
||||
_sessionId: string,
|
||||
_options?: GetSessionInfoOptions,
|
||||
): Promise<SDKSessionInfo | undefined> {
|
||||
throw new Error('getSessionInfo is not implemented in the SDK')
|
||||
}
|
||||
|
||||
/**
|
||||
* Rename a session. Appends a custom-title entry to the session's JSONL file.
|
||||
* @param sessionId - UUID of the session
|
||||
* @param title - New title
|
||||
* @param options - `{ dir?: string }` project path; omit to search all projects
|
||||
*/
|
||||
export async function renameSession(
|
||||
_sessionId: string,
|
||||
_title: string,
|
||||
_options?: SessionMutationOptions,
|
||||
): Promise<void> {
|
||||
throw new Error('renameSession is not implemented in the SDK')
|
||||
}
|
||||
|
||||
/**
|
||||
* Tag a session. Pass null to clear the tag.
|
||||
* @param sessionId - UUID of the session
|
||||
* @param tag - Tag string, or null to clear
|
||||
* @param options - `{ dir?: string }` project path; omit to search all projects
|
||||
*/
|
||||
export async function tagSession(
|
||||
_sessionId: string,
|
||||
_tag: string | null,
|
||||
_options?: SessionMutationOptions,
|
||||
): Promise<void> {
|
||||
throw new Error('tagSession is not implemented in the SDK')
|
||||
}
|
||||
|
||||
/**
|
||||
* Fork a session into a new branch with fresh UUIDs.
|
||||
*
|
||||
* Copies transcript messages from the source session into a new session file,
|
||||
* remapping every message UUID and preserving the parentUuid chain. Supports
|
||||
* `upToMessageId` for branching from a specific point in the conversation.
|
||||
*
|
||||
* Forked sessions start without undo history (file-history snapshots are not
|
||||
* copied).
|
||||
*
|
||||
* @param sessionId - UUID of the source session
|
||||
* @param options - `{ dir?, upToMessageId?, title? }`
|
||||
* @returns `{ sessionId }` — UUID of the new forked session
|
||||
*/
|
||||
export async function forkSession(
|
||||
_sessionId: string,
|
||||
_options?: ForkSessionOptions,
|
||||
): Promise<ForkSessionResult> {
|
||||
throw new Error('forkSession is not implemented in the SDK')
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Assistant daemon primitives (internal)
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* A scheduled task from `<dir>/.claude/scheduled_tasks.json`.
|
||||
* @internal
|
||||
*/
|
||||
export type CronTask = {
|
||||
id: string
|
||||
cron: string
|
||||
prompt: string
|
||||
createdAt: number
|
||||
recurring?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* Cron scheduler tuning knobs (jitter + expiry). Sourced at runtime from the
|
||||
* `tengu_kairos_cron_config` GrowthBook config in CLI sessions; daemon hosts
|
||||
* pass this through `watchScheduledTasks({ getJitterConfig })` to get the
|
||||
* same tuning.
|
||||
* @internal
|
||||
*/
|
||||
export type CronJitterConfig = {
|
||||
recurringFrac: number
|
||||
recurringCapMs: number
|
||||
oneShotMaxMs: number
|
||||
oneShotFloorMs: number
|
||||
oneShotMinuteMod: number
|
||||
recurringMaxAgeMs: number
|
||||
}
|
||||
|
||||
/**
|
||||
* Event yielded by `watchScheduledTasks()`.
|
||||
* @internal
|
||||
*/
|
||||
export type ScheduledTaskEvent =
|
||||
| { type: 'fire'; task: CronTask }
|
||||
| { type: 'missed'; tasks: CronTask[] }
|
||||
|
||||
/**
|
||||
* Handle returned by `watchScheduledTasks()`.
|
||||
* @internal
|
||||
*/
|
||||
export type ScheduledTasksHandle = {
|
||||
/** Async stream of fire/missed events. Drain with `for await`. */
|
||||
events(): AsyncGenerator<ScheduledTaskEvent>
|
||||
/**
|
||||
* Epoch ms of the soonest scheduled fire across all loaded tasks, or null
|
||||
* if nothing is scheduled. Useful for deciding whether to tear down an
|
||||
* idle agent subprocess or keep it warm for an imminent fire.
|
||||
*/
|
||||
getNextFireTime(): number | null
|
||||
}
|
||||
|
||||
/**
|
||||
* Watch `<dir>/.claude/scheduled_tasks.json` and yield events as tasks fire.
|
||||
*
|
||||
* Acquires the per-directory scheduler lock (PID-based liveness) so a REPL
|
||||
* session in the same dir won't double-fire. Releases the lock and closes
|
||||
* the file watcher when the signal aborts.
|
||||
*
|
||||
* - `fire` — a task whose cron schedule was met. One-shot tasks are already
|
||||
* deleted from the file when this yields; recurring tasks are rescheduled
|
||||
* (or deleted if aged out).
|
||||
* - `missed` — one-shot tasks whose window passed while the daemon was down.
|
||||
* Yielded once on initial load; a background delete removes them from the
|
||||
* file shortly after.
|
||||
*
|
||||
* Intended for daemon architectures that own the scheduler externally and
|
||||
* spawn the agent via `query()`; the agent subprocess (`-p` mode) does not
|
||||
* run its own scheduler.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export function watchScheduledTasks(_opts: {
|
||||
dir: string
|
||||
signal: AbortSignal
|
||||
getJitterConfig?: () => CronJitterConfig
|
||||
}): ScheduledTasksHandle {
|
||||
throw new Error('not implemented')
|
||||
}
|
||||
|
||||
/**
|
||||
* Format missed one-shot tasks into a prompt that asks the model to confirm
|
||||
* with the user (via AskUserQuestion) before executing.
|
||||
* @internal
|
||||
*/
|
||||
export function buildMissedTaskNotification(_missed: CronTask[]): string {
|
||||
throw new Error('not implemented')
|
||||
}
|
||||
|
||||
/**
|
||||
* A user message typed on claude.ai, extracted from the bridge WS.
|
||||
* @internal
|
||||
*/
|
||||
export type InboundPrompt = {
|
||||
content: string | unknown[]
|
||||
uuid?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Options for connectRemoteControl.
|
||||
* @internal
|
||||
*/
|
||||
export type ConnectRemoteControlOptions = {
|
||||
dir: string
|
||||
name?: string
|
||||
workerType?: string
|
||||
branch?: string
|
||||
gitRepoUrl?: string | null
|
||||
getAccessToken: () => string | undefined
|
||||
baseUrl: string
|
||||
orgUUID: string
|
||||
model: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle returned by connectRemoteControl. Write query() yields in,
|
||||
* read inbound prompts out. See src/assistant/daemonBridge.ts for full
|
||||
* field documentation.
|
||||
* @internal
|
||||
*/
|
||||
export type RemoteControlHandle = {
|
||||
sessionUrl: string
|
||||
environmentId: string
|
||||
bridgeSessionId: string
|
||||
write(msg: SDKMessage): void
|
||||
sendResult(): void
|
||||
sendControlRequest(req: unknown): void
|
||||
sendControlResponse(res: unknown): void
|
||||
sendControlCancelRequest(requestId: string): void
|
||||
inboundPrompts(): AsyncGenerator<InboundPrompt>
|
||||
controlRequests(): AsyncGenerator<unknown>
|
||||
permissionResponses(): AsyncGenerator<unknown>
|
||||
onStateChange(
|
||||
cb: (
|
||||
state: 'ready' | 'connected' | 'reconnecting' | 'failed',
|
||||
detail?: string,
|
||||
) => void,
|
||||
): void
|
||||
teardown(): Promise<void>
|
||||
}
|
||||
|
||||
/**
|
||||
* Hold a claude.ai remote-control bridge connection from a daemon process.
|
||||
*
|
||||
* The daemon owns the WebSocket in the PARENT process — if the agent
|
||||
* subprocess (spawned via `query()`) crashes, the daemon respawns it while
|
||||
* claude.ai keeps the same session. Contrast with `query.enableRemoteControl`
|
||||
* which puts the WS in the CHILD process (dies with the agent).
|
||||
*
|
||||
* Pipe `query()` yields through `write()` + `sendResult()`. Read
|
||||
* `inboundPrompts()` (user typed on claude.ai) into `query()`'s input
|
||||
* stream. Handle `controlRequests()` locally (interrupt → abort, set_model
|
||||
* → reconfigure).
|
||||
*
|
||||
* Skips the `tengu_ccr_bridge` gate and policy-limits check — @internal
|
||||
* caller is pre-entitled. OAuth is still required (env var or keychain).
|
||||
*
|
||||
* Returns null on no-OAuth or registration failure.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export async function connectRemoteControl(
|
||||
_opts: ConnectRemoteControlOptions,
|
||||
): Promise<RemoteControlHandle | null> {
|
||||
throw new Error('not implemented')
|
||||
}
|
||||
303
src/entrypoints/cli.tsx
Normal file
303
src/entrypoints/cli.tsx
Normal file
File diff suppressed because one or more lines are too long
340
src/entrypoints/init.ts
Normal file
340
src/entrypoints/init.ts
Normal file
@@ -0,0 +1,340 @@
|
||||
import { profileCheckpoint } from '../utils/startupProfiler.js'
|
||||
import '../bootstrap/state.js'
|
||||
import '../utils/config.js'
|
||||
import type { Attributes, MetricOptions } from '@opentelemetry/api'
|
||||
import memoize from 'lodash-es/memoize.js'
|
||||
import { getIsNonInteractiveSession } from 'src/bootstrap/state.js'
|
||||
import type { AttributedCounter } from '../bootstrap/state.js'
|
||||
import { getSessionCounter, setMeter } from '../bootstrap/state.js'
|
||||
import { shutdownLspServerManager } from '../services/lsp/manager.js'
|
||||
import { populateOAuthAccountInfoIfNeeded } from '../services/oauth/client.js'
|
||||
import {
|
||||
initializePolicyLimitsLoadingPromise,
|
||||
isPolicyLimitsEligible,
|
||||
} from '../services/policyLimits/index.js'
|
||||
import {
|
||||
initializeRemoteManagedSettingsLoadingPromise,
|
||||
isEligibleForRemoteManagedSettings,
|
||||
waitForRemoteManagedSettingsToLoad,
|
||||
} from '../services/remoteManagedSettings/index.js'
|
||||
import { preconnectAnthropicApi } from '../utils/apiPreconnect.js'
|
||||
import { applyExtraCACertsFromConfig } from '../utils/caCertsConfig.js'
|
||||
import { registerCleanup } from '../utils/cleanupRegistry.js'
|
||||
import { enableConfigs, recordFirstStartTime } from '../utils/config.js'
|
||||
import { logForDebugging } from '../utils/debug.js'
|
||||
import { detectCurrentRepository } from '../utils/detectRepository.js'
|
||||
import { logForDiagnosticsNoPII } from '../utils/diagLogs.js'
|
||||
import { initJetBrainsDetection } from '../utils/envDynamic.js'
|
||||
import { isEnvTruthy } from '../utils/envUtils.js'
|
||||
import { ConfigParseError, errorMessage } from '../utils/errors.js'
|
||||
// showInvalidConfigDialog is dynamically imported in the error path to avoid loading React at init
|
||||
import {
|
||||
gracefulShutdownSync,
|
||||
setupGracefulShutdown,
|
||||
} from '../utils/gracefulShutdown.js'
|
||||
import {
|
||||
applyConfigEnvironmentVariables,
|
||||
applySafeConfigEnvironmentVariables,
|
||||
} from '../utils/managedEnv.js'
|
||||
import { configureGlobalMTLS } from '../utils/mtls.js'
|
||||
import {
|
||||
ensureScratchpadDir,
|
||||
isScratchpadEnabled,
|
||||
} from '../utils/permissions/filesystem.js'
|
||||
// initializeTelemetry is loaded lazily via import() in setMeterState() to defer
|
||||
// ~400KB of OpenTelemetry + protobuf modules until telemetry is actually initialized.
|
||||
// gRPC exporters (~700KB via @grpc/grpc-js) are further lazy-loaded within instrumentation.ts.
|
||||
import { configureGlobalAgents } from '../utils/proxy.js'
|
||||
import { isBetaTracingEnabled } from '../utils/telemetry/betaSessionTracing.js'
|
||||
import { getTelemetryAttributes } from '../utils/telemetryAttributes.js'
|
||||
import { setShellIfWindows } from '../utils/windowsPaths.js'
|
||||
|
||||
// initialize1PEventLogging is dynamically imported to defer OpenTelemetry sdk-logs/resources
|
||||
|
||||
// Track if telemetry has been initialized to prevent double initialization
|
||||
let telemetryInitialized = false
|
||||
|
||||
export const init = memoize(async (): Promise<void> => {
|
||||
const initStartTime = Date.now()
|
||||
logForDiagnosticsNoPII('info', 'init_started')
|
||||
profileCheckpoint('init_function_start')
|
||||
|
||||
// Validate configs are valid and enable configuration system
|
||||
try {
|
||||
const configsStart = Date.now()
|
||||
enableConfigs()
|
||||
logForDiagnosticsNoPII('info', 'init_configs_enabled', {
|
||||
duration_ms: Date.now() - configsStart,
|
||||
})
|
||||
profileCheckpoint('init_configs_enabled')
|
||||
|
||||
// Apply only safe environment variables before trust dialog
|
||||
// Full environment variables are applied after trust is established
|
||||
const envVarsStart = Date.now()
|
||||
applySafeConfigEnvironmentVariables()
|
||||
|
||||
// Apply NODE_EXTRA_CA_CERTS from settings.json to process.env early,
|
||||
// before any TLS connections. Bun caches the TLS cert store at boot
|
||||
// via BoringSSL, so this must happen before the first TLS handshake.
|
||||
applyExtraCACertsFromConfig()
|
||||
|
||||
logForDiagnosticsNoPII('info', 'init_safe_env_vars_applied', {
|
||||
duration_ms: Date.now() - envVarsStart,
|
||||
})
|
||||
profileCheckpoint('init_safe_env_vars_applied')
|
||||
|
||||
// Make sure things get flushed on exit
|
||||
setupGracefulShutdown()
|
||||
profileCheckpoint('init_after_graceful_shutdown')
|
||||
|
||||
// Initialize 1P event logging (no security concerns, but deferred to avoid
|
||||
// loading OpenTelemetry sdk-logs at startup). growthbook.js is already in
|
||||
// the module cache by this point (firstPartyEventLogger imports it), so the
|
||||
// second dynamic import adds no load cost.
|
||||
void Promise.all([
|
||||
import('../services/analytics/firstPartyEventLogger.js'),
|
||||
import('../services/analytics/growthbook.js'),
|
||||
]).then(([fp, gb]) => {
|
||||
fp.initialize1PEventLogging()
|
||||
// Rebuild the logger provider if tengu_1p_event_batch_config changes
|
||||
// mid-session. Change detection (isEqual) is inside the handler so
|
||||
// unchanged refreshes are no-ops.
|
||||
gb.onGrowthBookRefresh(() => {
|
||||
void fp.reinitialize1PEventLoggingIfConfigChanged()
|
||||
})
|
||||
})
|
||||
profileCheckpoint('init_after_1p_event_logging')
|
||||
|
||||
// Populate OAuth account info if it is not already cached in config. This is needed since the
|
||||
// OAuth account info may not be populated when logging in through the VSCode extension.
|
||||
void populateOAuthAccountInfoIfNeeded()
|
||||
profileCheckpoint('init_after_oauth_populate')
|
||||
|
||||
// Initialize JetBrains IDE detection asynchronously (populates cache for later sync access)
|
||||
void initJetBrainsDetection()
|
||||
profileCheckpoint('init_after_jetbrains_detection')
|
||||
|
||||
// Detect GitHub repository asynchronously (populates cache for gitDiff PR linking)
|
||||
void detectCurrentRepository()
|
||||
|
||||
// Initialize the loading promise early so that other systems (like plugin hooks)
|
||||
// can await remote settings loading. The promise includes a timeout to prevent
|
||||
// deadlocks if loadRemoteManagedSettings() is never called (e.g., Agent SDK tests).
|
||||
if (isEligibleForRemoteManagedSettings()) {
|
||||
initializeRemoteManagedSettingsLoadingPromise()
|
||||
}
|
||||
if (isPolicyLimitsEligible()) {
|
||||
initializePolicyLimitsLoadingPromise()
|
||||
}
|
||||
profileCheckpoint('init_after_remote_settings_check')
|
||||
|
||||
// Record the first start time
|
||||
recordFirstStartTime()
|
||||
|
||||
// Configure global mTLS settings
|
||||
const mtlsStart = Date.now()
|
||||
logForDebugging('[init] configureGlobalMTLS starting')
|
||||
configureGlobalMTLS()
|
||||
logForDiagnosticsNoPII('info', 'init_mtls_configured', {
|
||||
duration_ms: Date.now() - mtlsStart,
|
||||
})
|
||||
logForDebugging('[init] configureGlobalMTLS complete')
|
||||
|
||||
// Configure global HTTP agents (proxy and/or mTLS)
|
||||
const proxyStart = Date.now()
|
||||
logForDebugging('[init] configureGlobalAgents starting')
|
||||
configureGlobalAgents()
|
||||
logForDiagnosticsNoPII('info', 'init_proxy_configured', {
|
||||
duration_ms: Date.now() - proxyStart,
|
||||
})
|
||||
logForDebugging('[init] configureGlobalAgents complete')
|
||||
profileCheckpoint('init_network_configured')
|
||||
|
||||
// Preconnect to the Anthropic API — overlap TCP+TLS handshake
|
||||
// (~100-200ms) with the ~100ms of action-handler work before the API
|
||||
// request. After CA certs + proxy agents are configured so the warmed
|
||||
// connection uses the right transport. Fire-and-forget; skipped for
|
||||
// proxy/mTLS/unix/cloud-provider where the SDK's dispatcher wouldn't
|
||||
// reuse the global pool.
|
||||
preconnectAnthropicApi()
|
||||
|
||||
// CCR upstreamproxy: start the local CONNECT relay so agent subprocesses
|
||||
// can reach org-configured upstreams with credential injection. Gated on
|
||||
// CLAUDE_CODE_REMOTE + GrowthBook; fail-open on any error. Lazy import so
|
||||
// non-CCR startups don't pay the module load. The getUpstreamProxyEnv
|
||||
// function is registered with subprocessEnv.ts so subprocess spawning can
|
||||
// inject proxy vars without a static import of the upstreamproxy module.
|
||||
if (isEnvTruthy(process.env.CLAUDE_CODE_REMOTE)) {
|
||||
try {
|
||||
const { initUpstreamProxy, getUpstreamProxyEnv } = await import(
|
||||
'../upstreamproxy/upstreamproxy.js'
|
||||
)
|
||||
const { registerUpstreamProxyEnvFn } = await import(
|
||||
'../utils/subprocessEnv.js'
|
||||
)
|
||||
registerUpstreamProxyEnvFn(getUpstreamProxyEnv)
|
||||
await initUpstreamProxy()
|
||||
} catch (err) {
|
||||
logForDebugging(
|
||||
`[init] upstreamproxy init failed: ${err instanceof Error ? err.message : String(err)}; continuing without proxy`,
|
||||
{ level: 'warn' },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Set up git-bash if relevant
|
||||
setShellIfWindows()
|
||||
|
||||
// Register LSP manager cleanup (initialization happens in main.tsx after --plugin-dir is processed)
|
||||
registerCleanup(shutdownLspServerManager)
|
||||
|
||||
// gh-32730: teams created by subagents (or main agent without
|
||||
// explicit TeamDelete) were left on disk forever. Register cleanup
|
||||
// for all teams created this session. Lazy import: swarm code is
|
||||
// behind feature gate and most sessions never create teams.
|
||||
registerCleanup(async () => {
|
||||
const { cleanupSessionTeams } = await import(
|
||||
'../utils/swarm/teamHelpers.js'
|
||||
)
|
||||
await cleanupSessionTeams()
|
||||
})
|
||||
|
||||
// Initialize scratchpad directory if enabled
|
||||
if (isScratchpadEnabled()) {
|
||||
const scratchpadStart = Date.now()
|
||||
await ensureScratchpadDir()
|
||||
logForDiagnosticsNoPII('info', 'init_scratchpad_created', {
|
||||
duration_ms: Date.now() - scratchpadStart,
|
||||
})
|
||||
}
|
||||
|
||||
logForDiagnosticsNoPII('info', 'init_completed', {
|
||||
duration_ms: Date.now() - initStartTime,
|
||||
})
|
||||
profileCheckpoint('init_function_end')
|
||||
} catch (error) {
|
||||
if (error instanceof ConfigParseError) {
|
||||
// Skip the interactive Ink dialog when we can't safely render it.
|
||||
// The dialog breaks JSON consumers (e.g. desktop marketplace plugin
|
||||
// manager running `plugin marketplace list --json` in a VM sandbox).
|
||||
if (getIsNonInteractiveSession()) {
|
||||
process.stderr.write(
|
||||
`Configuration error in ${error.filePath}: ${error.message}\n`,
|
||||
)
|
||||
gracefulShutdownSync(1)
|
||||
return
|
||||
}
|
||||
|
||||
// Show the invalid config dialog with the error object and wait for it to complete
|
||||
return import('../components/InvalidConfigDialog.js').then(m =>
|
||||
m.showInvalidConfigDialog({ error }),
|
||||
)
|
||||
// Dialog itself handles process.exit, so we don't need additional cleanup here
|
||||
} else {
|
||||
// For non-config errors, rethrow them
|
||||
throw error
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* Initialize telemetry after trust has been granted.
|
||||
* For remote-settings-eligible users, waits for settings to load (non-blocking),
|
||||
* then re-applies env vars (to include remote settings) before initializing telemetry.
|
||||
* For non-eligible users, initializes telemetry immediately.
|
||||
* This should only be called once, after the trust dialog has been accepted.
|
||||
*/
|
||||
export function initializeTelemetryAfterTrust(): void {
|
||||
if (isEligibleForRemoteManagedSettings()) {
|
||||
// For SDK/headless mode with beta tracing, initialize eagerly first
|
||||
// to ensure the tracer is ready before the first query runs.
|
||||
// The async path below will still run but doInitializeTelemetry() guards against double init.
|
||||
if (getIsNonInteractiveSession() && isBetaTracingEnabled()) {
|
||||
void doInitializeTelemetry().catch(error => {
|
||||
logForDebugging(
|
||||
`[3P telemetry] Eager telemetry init failed (beta tracing): ${errorMessage(error)}`,
|
||||
{ level: 'error' },
|
||||
)
|
||||
})
|
||||
}
|
||||
logForDebugging(
|
||||
'[3P telemetry] Waiting for remote managed settings before telemetry init',
|
||||
)
|
||||
void waitForRemoteManagedSettingsToLoad()
|
||||
.then(async () => {
|
||||
logForDebugging(
|
||||
'[3P telemetry] Remote managed settings loaded, initializing telemetry',
|
||||
)
|
||||
// Re-apply env vars to pick up remote settings before initializing telemetry.
|
||||
applyConfigEnvironmentVariables()
|
||||
await doInitializeTelemetry()
|
||||
})
|
||||
.catch(error => {
|
||||
logForDebugging(
|
||||
`[3P telemetry] Telemetry init failed (remote settings path): ${errorMessage(error)}`,
|
||||
{ level: 'error' },
|
||||
)
|
||||
})
|
||||
} else {
|
||||
void doInitializeTelemetry().catch(error => {
|
||||
logForDebugging(
|
||||
`[3P telemetry] Telemetry init failed: ${errorMessage(error)}`,
|
||||
{ level: 'error' },
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
async function doInitializeTelemetry(): Promise<void> {
|
||||
if (telemetryInitialized) {
|
||||
// Already initialized, nothing to do
|
||||
return
|
||||
}
|
||||
|
||||
// Set flag before init to prevent double initialization
|
||||
telemetryInitialized = true
|
||||
try {
|
||||
await setMeterState()
|
||||
} catch (error) {
|
||||
// Reset flag on failure so subsequent calls can retry
|
||||
telemetryInitialized = false
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
async function setMeterState(): Promise<void> {
|
||||
// Lazy-load instrumentation to defer ~400KB of OpenTelemetry + protobuf
|
||||
const { initializeTelemetry } = await import(
|
||||
'../utils/telemetry/instrumentation.js'
|
||||
)
|
||||
// Initialize customer OTLP telemetry (metrics, logs, traces)
|
||||
const meter = await initializeTelemetry()
|
||||
if (meter) {
|
||||
// Create factory function for attributed counters
|
||||
const createAttributedCounter = (
|
||||
name: string,
|
||||
options: MetricOptions,
|
||||
): AttributedCounter => {
|
||||
const counter = meter?.createCounter(name, options)
|
||||
|
||||
return {
|
||||
add(value: number, additionalAttributes: Attributes = {}) {
|
||||
// Always fetch fresh telemetry attributes to ensure they're up to date
|
||||
const currentAttributes = getTelemetryAttributes()
|
||||
const mergedAttributes = {
|
||||
...currentAttributes,
|
||||
...additionalAttributes,
|
||||
}
|
||||
counter?.add(value, mergedAttributes)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
setMeter(meter, createAttributedCounter)
|
||||
|
||||
// Increment session counter here because the startup telemetry path
|
||||
// runs before this async initialization completes, so the counter
|
||||
// would be null there.
|
||||
getSessionCounter()?.add(1)
|
||||
}
|
||||
}
|
||||
196
src/entrypoints/mcp.ts
Normal file
196
src/entrypoints/mcp.ts
Normal file
@@ -0,0 +1,196 @@
|
||||
import { Server } from '@modelcontextprotocol/sdk/server/index.js'
|
||||
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
|
||||
import {
|
||||
CallToolRequestSchema,
|
||||
type CallToolResult,
|
||||
ListToolsRequestSchema,
|
||||
type ListToolsResult,
|
||||
type Tool,
|
||||
} from '@modelcontextprotocol/sdk/types.js'
|
||||
import { getDefaultAppState } from 'src/state/AppStateStore.js'
|
||||
import review from '../commands/review.js'
|
||||
import type { Command } from '../commands.js'
|
||||
import {
|
||||
findToolByName,
|
||||
getEmptyToolPermissionContext,
|
||||
type ToolUseContext,
|
||||
} from '../Tool.js'
|
||||
import { getTools } from '../tools.js'
|
||||
import { createAbortController } from '../utils/abortController.js'
|
||||
import { createFileStateCacheWithSizeLimit } from '../utils/fileStateCache.js'
|
||||
import { logError } from '../utils/log.js'
|
||||
import { createAssistantMessage } from '../utils/messages.js'
|
||||
import { getMainLoopModel } from '../utils/model/model.js'
|
||||
import { hasPermissionsToUseTool } from '../utils/permissions/permissions.js'
|
||||
import { setCwd } from '../utils/Shell.js'
|
||||
import { jsonStringify } from '../utils/slowOperations.js'
|
||||
import { getErrorParts } from '../utils/toolErrors.js'
|
||||
import { zodToJsonSchema } from '../utils/zodToJsonSchema.js'
|
||||
|
||||
type ToolInput = Tool['inputSchema']
|
||||
type ToolOutput = Tool['outputSchema']
|
||||
|
||||
const MCP_COMMANDS: Command[] = [review]
|
||||
|
||||
export async function startMCPServer(
|
||||
cwd: string,
|
||||
debug: boolean,
|
||||
verbose: boolean,
|
||||
): Promise<void> {
|
||||
// Use size-limited LRU cache for readFileState to prevent unbounded memory growth
|
||||
// 100 files and 25MB limit should be sufficient for MCP server operations
|
||||
const READ_FILE_STATE_CACHE_SIZE = 100
|
||||
const readFileStateCache = createFileStateCacheWithSizeLimit(
|
||||
READ_FILE_STATE_CACHE_SIZE,
|
||||
)
|
||||
setCwd(cwd)
|
||||
const server = new Server(
|
||||
{
|
||||
name: 'claude/tengu',
|
||||
version: MACRO.VERSION,
|
||||
},
|
||||
{
|
||||
capabilities: {
|
||||
tools: {},
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
server.setRequestHandler(
|
||||
ListToolsRequestSchema,
|
||||
async (): Promise<ListToolsResult> => {
|
||||
// TODO: Also re-expose any MCP tools
|
||||
const toolPermissionContext = getEmptyToolPermissionContext()
|
||||
const tools = getTools(toolPermissionContext)
|
||||
return {
|
||||
tools: await Promise.all(
|
||||
tools.map(async tool => {
|
||||
let outputSchema: ToolOutput | undefined
|
||||
if (tool.outputSchema) {
|
||||
const convertedSchema = zodToJsonSchema(tool.outputSchema)
|
||||
// MCP SDK requires outputSchema to have type: "object" at root level
|
||||
// Skip schemas with anyOf/oneOf at root (from z.union, z.discriminatedUnion, etc.)
|
||||
// See: https://github.com/anthropics/claude-code/issues/8014
|
||||
if (
|
||||
typeof convertedSchema === 'object' &&
|
||||
convertedSchema !== null &&
|
||||
'type' in convertedSchema &&
|
||||
convertedSchema.type === 'object'
|
||||
) {
|
||||
outputSchema = convertedSchema as ToolOutput
|
||||
}
|
||||
}
|
||||
return {
|
||||
...tool,
|
||||
description: await tool.prompt({
|
||||
getToolPermissionContext: async () => toolPermissionContext,
|
||||
tools,
|
||||
agents: [],
|
||||
}),
|
||||
inputSchema: zodToJsonSchema(tool.inputSchema) as ToolInput,
|
||||
outputSchema,
|
||||
}
|
||||
}),
|
||||
),
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
server.setRequestHandler(
|
||||
CallToolRequestSchema,
|
||||
async ({ params: { name, arguments: args } }): Promise<CallToolResult> => {
|
||||
const toolPermissionContext = getEmptyToolPermissionContext()
|
||||
// TODO: Also re-expose any MCP tools
|
||||
const tools = getTools(toolPermissionContext)
|
||||
const tool = findToolByName(tools, name)
|
||||
if (!tool) {
|
||||
throw new Error(`Tool ${name} not found`)
|
||||
}
|
||||
|
||||
// Assume MCP servers do not read messages separately from the tool
|
||||
// call arguments.
|
||||
const toolUseContext: ToolUseContext = {
|
||||
abortController: createAbortController(),
|
||||
options: {
|
||||
commands: MCP_COMMANDS,
|
||||
tools,
|
||||
mainLoopModel: getMainLoopModel(),
|
||||
thinkingConfig: { type: 'disabled' },
|
||||
mcpClients: [],
|
||||
mcpResources: {},
|
||||
isNonInteractiveSession: true,
|
||||
debug,
|
||||
verbose,
|
||||
agentDefinitions: { activeAgents: [], allAgents: [] },
|
||||
},
|
||||
getAppState: () => getDefaultAppState(),
|
||||
setAppState: () => {},
|
||||
messages: [],
|
||||
readFileState: readFileStateCache,
|
||||
setInProgressToolUseIDs: () => {},
|
||||
setResponseLength: () => {},
|
||||
updateFileHistoryState: () => {},
|
||||
updateAttributionState: () => {},
|
||||
}
|
||||
|
||||
// TODO: validate input types with zod
|
||||
try {
|
||||
if (!tool.isEnabled()) {
|
||||
throw new Error(`Tool ${name} is not enabled`)
|
||||
}
|
||||
const validationResult = await tool.validateInput?.(
|
||||
(args as never) ?? {},
|
||||
toolUseContext,
|
||||
)
|
||||
if (validationResult && !validationResult.result) {
|
||||
throw new Error(
|
||||
`Tool ${name} input is invalid: ${validationResult.message}`,
|
||||
)
|
||||
}
|
||||
const finalResult = await tool.call(
|
||||
(args ?? {}) as never,
|
||||
toolUseContext,
|
||||
hasPermissionsToUseTool,
|
||||
createAssistantMessage({
|
||||
content: [],
|
||||
}),
|
||||
)
|
||||
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'text' as const,
|
||||
text:
|
||||
typeof finalResult === 'string'
|
||||
? finalResult
|
||||
: jsonStringify(finalResult.data),
|
||||
},
|
||||
],
|
||||
}
|
||||
} catch (error) {
|
||||
logError(error)
|
||||
|
||||
const parts =
|
||||
error instanceof Error ? getErrorParts(error) : [String(error)]
|
||||
const errorText = parts.filter(Boolean).join('\n').trim() || 'Error'
|
||||
|
||||
return {
|
||||
isError: true,
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: errorText,
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
async function runServer() {
|
||||
const transport = new StdioServerTransport()
|
||||
await server.connect(transport)
|
||||
}
|
||||
|
||||
return await runServer()
|
||||
}
|
||||
156
src/entrypoints/sandboxTypes.ts
Normal file
156
src/entrypoints/sandboxTypes.ts
Normal file
@@ -0,0 +1,156 @@
|
||||
/**
|
||||
* Sandbox types for the Claude Code Agent SDK
|
||||
*
|
||||
* This file is the single source of truth for sandbox configuration types.
|
||||
* Both the SDK and the settings validation import from here.
|
||||
*/
|
||||
|
||||
import { z } from 'zod/v4'
|
||||
import { lazySchema } from '../utils/lazySchema.js'
|
||||
|
||||
/**
|
||||
* Network configuration schema for sandbox.
|
||||
*/
|
||||
export const SandboxNetworkConfigSchema = lazySchema(() =>
|
||||
z
|
||||
.object({
|
||||
allowedDomains: z.array(z.string()).optional(),
|
||||
allowManagedDomainsOnly: z
|
||||
.boolean()
|
||||
.optional()
|
||||
.describe(
|
||||
'When true (and set in managed settings), only allowedDomains and WebFetch(domain:...) allow rules from managed settings are respected. ' +
|
||||
'User, project, local, and flag settings domains are ignored. Denied domains are still respected from all sources.',
|
||||
),
|
||||
allowUnixSockets: z
|
||||
.array(z.string())
|
||||
.optional()
|
||||
.describe(
|
||||
'macOS only: Unix socket paths to allow. Ignored on Linux (seccomp cannot filter by path).',
|
||||
),
|
||||
allowAllUnixSockets: z
|
||||
.boolean()
|
||||
.optional()
|
||||
.describe(
|
||||
'If true, allow all Unix sockets (disables blocking on both platforms).',
|
||||
),
|
||||
allowLocalBinding: z.boolean().optional(),
|
||||
httpProxyPort: z.number().optional(),
|
||||
socksProxyPort: z.number().optional(),
|
||||
})
|
||||
.optional(),
|
||||
)
|
||||
|
||||
/**
|
||||
* Filesystem configuration schema for sandbox.
|
||||
*/
|
||||
export const SandboxFilesystemConfigSchema = lazySchema(() =>
|
||||
z
|
||||
.object({
|
||||
allowWrite: z
|
||||
.array(z.string())
|
||||
.optional()
|
||||
.describe(
|
||||
'Additional paths to allow writing within the sandbox. ' +
|
||||
'Merged with paths from Edit(...) allow permission rules.',
|
||||
),
|
||||
denyWrite: z
|
||||
.array(z.string())
|
||||
.optional()
|
||||
.describe(
|
||||
'Additional paths to deny writing within the sandbox. ' +
|
||||
'Merged with paths from Edit(...) deny permission rules.',
|
||||
),
|
||||
denyRead: z
|
||||
.array(z.string())
|
||||
.optional()
|
||||
.describe(
|
||||
'Additional paths to deny reading within the sandbox. ' +
|
||||
'Merged with paths from Read(...) deny permission rules.',
|
||||
),
|
||||
allowRead: z
|
||||
.array(z.string())
|
||||
.optional()
|
||||
.describe(
|
||||
'Paths to re-allow reading within denyRead regions. ' +
|
||||
'Takes precedence over denyRead for matching paths.',
|
||||
),
|
||||
allowManagedReadPathsOnly: z
|
||||
.boolean()
|
||||
.optional()
|
||||
.describe(
|
||||
'When true (set in managed settings), only allowRead paths from policySettings are used.',
|
||||
),
|
||||
})
|
||||
.optional(),
|
||||
)
|
||||
|
||||
/**
|
||||
* Sandbox settings schema.
|
||||
*/
|
||||
export const SandboxSettingsSchema = lazySchema(() =>
|
||||
z
|
||||
.object({
|
||||
enabled: z.boolean().optional(),
|
||||
failIfUnavailable: z
|
||||
.boolean()
|
||||
.optional()
|
||||
.describe(
|
||||
'Exit with an error at startup if sandbox.enabled is true but the sandbox cannot start ' +
|
||||
'(missing dependencies, unsupported platform, or platform not in enabledPlatforms). ' +
|
||||
'When false (default), a warning is shown and commands run unsandboxed. ' +
|
||||
'Intended for managed-settings deployments that require sandboxing as a hard gate.',
|
||||
),
|
||||
// Note: enabledPlatforms is an undocumented setting read via .passthrough()
|
||||
// It restricts sandboxing to specific platforms (e.g., ["macos"]).
|
||||
//
|
||||
// Added to unblock NVIDIA enterprise rollout: they want to enable
|
||||
// autoAllowBashIfSandboxed but only on macOS initially, since Linux/WSL
|
||||
// sandbox support is newer and less battle-tested. This allows them to
|
||||
// set enabledPlatforms: ["macos"] to disable sandbox (and auto-allow)
|
||||
// on other platforms until they're ready to expand.
|
||||
autoAllowBashIfSandboxed: z.boolean().optional(),
|
||||
allowUnsandboxedCommands: z
|
||||
.boolean()
|
||||
.optional()
|
||||
.describe(
|
||||
'Allow commands to run outside the sandbox via the dangerouslyDisableSandbox parameter. ' +
|
||||
'When false, the dangerouslyDisableSandbox parameter is completely ignored and all commands must run sandboxed. ' +
|
||||
'Default: true.',
|
||||
),
|
||||
network: SandboxNetworkConfigSchema(),
|
||||
filesystem: SandboxFilesystemConfigSchema(),
|
||||
ignoreViolations: z.record(z.string(), z.array(z.string())).optional(),
|
||||
enableWeakerNestedSandbox: z.boolean().optional(),
|
||||
enableWeakerNetworkIsolation: z
|
||||
.boolean()
|
||||
.optional()
|
||||
.describe(
|
||||
'macOS only: Allow access to com.apple.trustd.agent in the sandbox. ' +
|
||||
'Needed for Go-based CLI tools (gh, gcloud, terraform, etc.) to verify TLS certificates ' +
|
||||
'when using httpProxyPort with a MITM proxy and custom CA. ' +
|
||||
'**Reduces security** — opens a potential data exfiltration vector through the trustd service. Default: false',
|
||||
),
|
||||
excludedCommands: z.array(z.string()).optional(),
|
||||
ripgrep: z
|
||||
.object({
|
||||
command: z.string(),
|
||||
args: z.array(z.string()).optional(),
|
||||
})
|
||||
.optional()
|
||||
.describe('Custom ripgrep configuration for bundled ripgrep support'),
|
||||
})
|
||||
.passthrough(),
|
||||
)
|
||||
|
||||
// Inferred types from schemas
|
||||
export type SandboxSettings = z.infer<ReturnType<typeof SandboxSettingsSchema>>
|
||||
export type SandboxNetworkConfig = NonNullable<
|
||||
z.infer<ReturnType<typeof SandboxNetworkConfigSchema>>
|
||||
>
|
||||
export type SandboxFilesystemConfig = NonNullable<
|
||||
z.infer<ReturnType<typeof SandboxFilesystemConfigSchema>>
|
||||
>
|
||||
export type SandboxIgnoreViolations = NonNullable<
|
||||
SandboxSettings['ignoreViolations']
|
||||
>
|
||||
663
src/entrypoints/sdk/controlSchemas.ts
Normal file
663
src/entrypoints/sdk/controlSchemas.ts
Normal file
@@ -0,0 +1,663 @@
|
||||
/**
|
||||
* SDK Control Schemas - Zod schemas for the control protocol.
|
||||
*
|
||||
* These schemas define the control protocol between SDK implementations and the CLI.
|
||||
* Used by SDK builders (e.g., Python SDK) to communicate with the CLI process.
|
||||
*
|
||||
* SDK consumers should use coreSchemas.ts instead.
|
||||
*/
|
||||
|
||||
import { z } from 'zod/v4'
|
||||
import { lazySchema } from '../../utils/lazySchema.js'
|
||||
import {
|
||||
AccountInfoSchema,
|
||||
AgentDefinitionSchema,
|
||||
AgentInfoSchema,
|
||||
FastModeStateSchema,
|
||||
HookEventSchema,
|
||||
HookInputSchema,
|
||||
McpServerConfigForProcessTransportSchema,
|
||||
McpServerStatusSchema,
|
||||
ModelInfoSchema,
|
||||
PermissionModeSchema,
|
||||
PermissionUpdateSchema,
|
||||
SDKMessageSchema,
|
||||
SDKPostTurnSummaryMessageSchema,
|
||||
SDKStreamlinedTextMessageSchema,
|
||||
SDKStreamlinedToolUseSummaryMessageSchema,
|
||||
SDKUserMessageSchema,
|
||||
SlashCommandSchema,
|
||||
} from './coreSchemas.js'
|
||||
|
||||
// ============================================================================
|
||||
// External Type Placeholders
|
||||
// ============================================================================
|
||||
|
||||
// JSONRPCMessage from @modelcontextprotocol/sdk - treat as unknown
|
||||
export const JSONRPCMessagePlaceholder = lazySchema(() => z.unknown())
|
||||
|
||||
// ============================================================================
|
||||
// Hook Callback Types
|
||||
// ============================================================================
|
||||
|
||||
export const SDKHookCallbackMatcherSchema = lazySchema(() =>
|
||||
z
|
||||
.object({
|
||||
matcher: z.string().optional(),
|
||||
hookCallbackIds: z.array(z.string()),
|
||||
timeout: z.number().optional(),
|
||||
})
|
||||
.describe('Configuration for matching and routing hook callbacks.'),
|
||||
)
|
||||
|
||||
// ============================================================================
|
||||
// Control Request Types
|
||||
// ============================================================================
|
||||
|
||||
export const SDKControlInitializeRequestSchema = lazySchema(() =>
|
||||
z
|
||||
.object({
|
||||
subtype: z.literal('initialize'),
|
||||
hooks: z
|
||||
.record(HookEventSchema(), z.array(SDKHookCallbackMatcherSchema()))
|
||||
.optional(),
|
||||
sdkMcpServers: z.array(z.string()).optional(),
|
||||
jsonSchema: z.record(z.string(), z.unknown()).optional(),
|
||||
systemPrompt: z.string().optional(),
|
||||
appendSystemPrompt: z.string().optional(),
|
||||
agents: z.record(z.string(), AgentDefinitionSchema()).optional(),
|
||||
promptSuggestions: z.boolean().optional(),
|
||||
agentProgressSummaries: z.boolean().optional(),
|
||||
})
|
||||
.describe(
|
||||
'Initializes the SDK session with hooks, MCP servers, and agent configuration.',
|
||||
),
|
||||
)
|
||||
|
||||
export const SDKControlInitializeResponseSchema = lazySchema(() =>
|
||||
z
|
||||
.object({
|
||||
commands: z.array(SlashCommandSchema()),
|
||||
agents: z.array(AgentInfoSchema()),
|
||||
output_style: z.string(),
|
||||
available_output_styles: z.array(z.string()),
|
||||
models: z.array(ModelInfoSchema()),
|
||||
account: AccountInfoSchema(),
|
||||
pid: z
|
||||
.number()
|
||||
.optional()
|
||||
.describe('@internal CLI process PID for tmux socket isolation'),
|
||||
fast_mode_state: FastModeStateSchema().optional(),
|
||||
})
|
||||
.describe(
|
||||
'Response from session initialization with available commands, models, and account info.',
|
||||
),
|
||||
)
|
||||
|
||||
export const SDKControlInterruptRequestSchema = lazySchema(() =>
|
||||
z
|
||||
.object({
|
||||
subtype: z.literal('interrupt'),
|
||||
})
|
||||
.describe('Interrupts the currently running conversation turn.'),
|
||||
)
|
||||
|
||||
|
||||
export const SDKControlPermissionRequestSchema = lazySchema(() =>
|
||||
z
|
||||
.object({
|
||||
subtype: z.literal('can_use_tool'),
|
||||
tool_name: z.string(),
|
||||
input: z.record(z.string(), z.unknown()),
|
||||
permission_suggestions: z.array(PermissionUpdateSchema()).optional(),
|
||||
blocked_path: z.string().optional(),
|
||||
decision_reason: z.string().optional(),
|
||||
title: z.string().optional(),
|
||||
display_name: z.string().optional(),
|
||||
tool_use_id: z.string(),
|
||||
agent_id: z.string().optional(),
|
||||
description: z.string().optional(),
|
||||
})
|
||||
.describe('Requests permission to use a tool with the given input.'),
|
||||
)
|
||||
|
||||
export const SDKControlSetPermissionModeRequestSchema = lazySchema(() =>
|
||||
z
|
||||
.object({
|
||||
subtype: z.literal('set_permission_mode'),
|
||||
mode: PermissionModeSchema(),
|
||||
ultraplan: z
|
||||
.boolean()
|
||||
.optional()
|
||||
.describe('@internal CCR ultraplan session marker.'),
|
||||
})
|
||||
.describe('Sets the permission mode for tool execution handling.'),
|
||||
)
|
||||
|
||||
export const SDKControlSetModelRequestSchema = lazySchema(() =>
|
||||
z
|
||||
.object({
|
||||
subtype: z.literal('set_model'),
|
||||
model: z.string().optional(),
|
||||
})
|
||||
.describe('Sets the model to use for subsequent conversation turns.'),
|
||||
)
|
||||
|
||||
export const SDKControlSetMaxThinkingTokensRequestSchema = lazySchema(() =>
|
||||
z
|
||||
.object({
|
||||
subtype: z.literal('set_max_thinking_tokens'),
|
||||
max_thinking_tokens: z.number().nullable(),
|
||||
})
|
||||
.describe(
|
||||
'Sets the maximum number of thinking tokens for extended thinking.',
|
||||
),
|
||||
)
|
||||
|
||||
export const SDKControlMcpStatusRequestSchema = lazySchema(() =>
|
||||
z
|
||||
.object({
|
||||
subtype: z.literal('mcp_status'),
|
||||
})
|
||||
.describe('Requests the current status of all MCP server connections.'),
|
||||
)
|
||||
|
||||
export const SDKControlMcpStatusResponseSchema = lazySchema(() =>
|
||||
z
|
||||
.object({
|
||||
mcpServers: z.array(McpServerStatusSchema()),
|
||||
})
|
||||
.describe(
|
||||
'Response containing the current status of all MCP server connections.',
|
||||
),
|
||||
)
|
||||
|
||||
export const SDKControlGetContextUsageRequestSchema = lazySchema(() =>
|
||||
z
|
||||
.object({
|
||||
subtype: z.literal('get_context_usage'),
|
||||
})
|
||||
.describe(
|
||||
'Requests a breakdown of current context window usage by category.',
|
||||
),
|
||||
)
|
||||
|
||||
const ContextCategorySchema = lazySchema(() =>
|
||||
z.object({
|
||||
name: z.string(),
|
||||
tokens: z.number(),
|
||||
color: z.string(),
|
||||
isDeferred: z.boolean().optional(),
|
||||
}),
|
||||
)
|
||||
|
||||
const ContextGridSquareSchema = lazySchema(() =>
|
||||
z.object({
|
||||
color: z.string(),
|
||||
isFilled: z.boolean(),
|
||||
categoryName: z.string(),
|
||||
tokens: z.number(),
|
||||
percentage: z.number(),
|
||||
squareFullness: z.number(),
|
||||
}),
|
||||
)
|
||||
|
||||
export const SDKControlGetContextUsageResponseSchema = lazySchema(() =>
|
||||
z
|
||||
.object({
|
||||
categories: z.array(ContextCategorySchema()),
|
||||
totalTokens: z.number(),
|
||||
maxTokens: z.number(),
|
||||
rawMaxTokens: z.number(),
|
||||
percentage: z.number(),
|
||||
gridRows: z.array(z.array(ContextGridSquareSchema())),
|
||||
model: z.string(),
|
||||
memoryFiles: z.array(
|
||||
z.object({
|
||||
path: z.string(),
|
||||
type: z.string(),
|
||||
tokens: z.number(),
|
||||
}),
|
||||
),
|
||||
mcpTools: z.array(
|
||||
z.object({
|
||||
name: z.string(),
|
||||
serverName: z.string(),
|
||||
tokens: z.number(),
|
||||
isLoaded: z.boolean().optional(),
|
||||
}),
|
||||
),
|
||||
deferredBuiltinTools: z
|
||||
.array(
|
||||
z.object({
|
||||
name: z.string(),
|
||||
tokens: z.number(),
|
||||
isLoaded: z.boolean(),
|
||||
}),
|
||||
)
|
||||
.optional(),
|
||||
systemTools: z
|
||||
.array(z.object({ name: z.string(), tokens: z.number() }))
|
||||
.optional(),
|
||||
systemPromptSections: z
|
||||
.array(z.object({ name: z.string(), tokens: z.number() }))
|
||||
.optional(),
|
||||
agents: z.array(
|
||||
z.object({
|
||||
agentType: z.string(),
|
||||
source: z.string(),
|
||||
tokens: z.number(),
|
||||
}),
|
||||
),
|
||||
slashCommands: z
|
||||
.object({
|
||||
totalCommands: z.number(),
|
||||
includedCommands: z.number(),
|
||||
tokens: z.number(),
|
||||
})
|
||||
.optional(),
|
||||
skills: z
|
||||
.object({
|
||||
totalSkills: z.number(),
|
||||
includedSkills: z.number(),
|
||||
tokens: z.number(),
|
||||
skillFrontmatter: z.array(
|
||||
z.object({
|
||||
name: z.string(),
|
||||
source: z.string(),
|
||||
tokens: z.number(),
|
||||
}),
|
||||
),
|
||||
})
|
||||
.optional(),
|
||||
autoCompactThreshold: z.number().optional(),
|
||||
isAutoCompactEnabled: z.boolean(),
|
||||
messageBreakdown: z
|
||||
.object({
|
||||
toolCallTokens: z.number(),
|
||||
toolResultTokens: z.number(),
|
||||
attachmentTokens: z.number(),
|
||||
assistantMessageTokens: z.number(),
|
||||
userMessageTokens: z.number(),
|
||||
toolCallsByType: z.array(
|
||||
z.object({
|
||||
name: z.string(),
|
||||
callTokens: z.number(),
|
||||
resultTokens: z.number(),
|
||||
}),
|
||||
),
|
||||
attachmentsByType: z.array(
|
||||
z.object({ name: z.string(), tokens: z.number() }),
|
||||
),
|
||||
})
|
||||
.optional(),
|
||||
apiUsage: z
|
||||
.object({
|
||||
input_tokens: z.number(),
|
||||
output_tokens: z.number(),
|
||||
cache_creation_input_tokens: z.number(),
|
||||
cache_read_input_tokens: z.number(),
|
||||
})
|
||||
.nullable(),
|
||||
})
|
||||
.describe(
|
||||
'Breakdown of current context window usage by category (system prompt, tools, messages, etc.).',
|
||||
),
|
||||
)
|
||||
|
||||
export const SDKControlRewindFilesRequestSchema = lazySchema(() =>
|
||||
z
|
||||
.object({
|
||||
subtype: z.literal('rewind_files'),
|
||||
user_message_id: z.string(),
|
||||
dry_run: z.boolean().optional(),
|
||||
})
|
||||
.describe('Rewinds file changes made since a specific user message.'),
|
||||
)
|
||||
|
||||
export const SDKControlRewindFilesResponseSchema = lazySchema(() =>
|
||||
z
|
||||
.object({
|
||||
canRewind: z.boolean(),
|
||||
error: z.string().optional(),
|
||||
filesChanged: z.array(z.string()).optional(),
|
||||
insertions: z.number().optional(),
|
||||
deletions: z.number().optional(),
|
||||
})
|
||||
.describe('Result of a rewindFiles operation.'),
|
||||
)
|
||||
|
||||
export const SDKControlCancelAsyncMessageRequestSchema = lazySchema(() =>
|
||||
z
|
||||
.object({
|
||||
subtype: z.literal('cancel_async_message'),
|
||||
message_uuid: z.string(),
|
||||
})
|
||||
.describe(
|
||||
'Drops a pending async user message from the command queue by uuid. No-op if already dequeued for execution.',
|
||||
),
|
||||
)
|
||||
|
||||
export const SDKControlCancelAsyncMessageResponseSchema = lazySchema(() =>
|
||||
z
|
||||
.object({
|
||||
cancelled: z.boolean(),
|
||||
})
|
||||
.describe(
|
||||
'Result of a cancel_async_message operation. cancelled=false means the message was not in the queue (already dequeued or never enqueued).',
|
||||
),
|
||||
)
|
||||
|
||||
export const SDKControlSeedReadStateRequestSchema = lazySchema(() =>
|
||||
z
|
||||
.object({
|
||||
subtype: z.literal('seed_read_state'),
|
||||
path: z.string(),
|
||||
mtime: z.number(),
|
||||
})
|
||||
.describe(
|
||||
'Seeds the readFileState cache with a path+mtime entry. Use when a prior Read was removed from context (e.g. by snip) so Edit validation would fail despite the client having observed the Read. The mtime lets the CLI detect if the file changed since the seeded Read — same staleness check as the normal path.',
|
||||
),
|
||||
)
|
||||
|
||||
export const SDKHookCallbackRequestSchema = lazySchema(() =>
|
||||
z
|
||||
.object({
|
||||
subtype: z.literal('hook_callback'),
|
||||
callback_id: z.string(),
|
||||
input: HookInputSchema(),
|
||||
tool_use_id: z.string().optional(),
|
||||
})
|
||||
.describe('Delivers a hook callback with its input data.'),
|
||||
)
|
||||
|
||||
export const SDKControlMcpMessageRequestSchema = lazySchema(() =>
|
||||
z
|
||||
.object({
|
||||
subtype: z.literal('mcp_message'),
|
||||
server_name: z.string(),
|
||||
message: JSONRPCMessagePlaceholder(),
|
||||
})
|
||||
.describe('Sends a JSON-RPC message to a specific MCP server.'),
|
||||
)
|
||||
|
||||
export const SDKControlMcpSetServersRequestSchema = lazySchema(() =>
|
||||
z
|
||||
.object({
|
||||
subtype: z.literal('mcp_set_servers'),
|
||||
servers: z.record(z.string(), McpServerConfigForProcessTransportSchema()),
|
||||
})
|
||||
.describe('Replaces the set of dynamically managed MCP servers.'),
|
||||
)
|
||||
|
||||
export const SDKControlMcpSetServersResponseSchema = lazySchema(() =>
|
||||
z
|
||||
.object({
|
||||
added: z.array(z.string()),
|
||||
removed: z.array(z.string()),
|
||||
errors: z.record(z.string(), z.string()),
|
||||
})
|
||||
.describe(
|
||||
'Result of replacing the set of dynamically managed MCP servers.',
|
||||
),
|
||||
)
|
||||
|
||||
export const SDKControlReloadPluginsRequestSchema = lazySchema(() =>
|
||||
z
|
||||
.object({
|
||||
subtype: z.literal('reload_plugins'),
|
||||
})
|
||||
.describe(
|
||||
'Reloads plugins from disk and returns the refreshed session components.',
|
||||
),
|
||||
)
|
||||
|
||||
export const SDKControlReloadPluginsResponseSchema = lazySchema(() =>
|
||||
z
|
||||
.object({
|
||||
commands: z.array(SlashCommandSchema()),
|
||||
agents: z.array(AgentInfoSchema()),
|
||||
plugins: z.array(
|
||||
z.object({
|
||||
name: z.string(),
|
||||
path: z.string(),
|
||||
source: z.string().optional(),
|
||||
}),
|
||||
),
|
||||
mcpServers: z.array(McpServerStatusSchema()),
|
||||
error_count: z.number(),
|
||||
})
|
||||
.describe(
|
||||
'Refreshed commands, agents, plugins, and MCP server status after reload.',
|
||||
),
|
||||
)
|
||||
|
||||
export const SDKControlMcpReconnectRequestSchema = lazySchema(() =>
|
||||
z
|
||||
.object({
|
||||
subtype: z.literal('mcp_reconnect'),
|
||||
serverName: z.string(),
|
||||
})
|
||||
.describe('Reconnects a disconnected or failed MCP server.'),
|
||||
)
|
||||
|
||||
export const SDKControlMcpToggleRequestSchema = lazySchema(() =>
|
||||
z
|
||||
.object({
|
||||
subtype: z.literal('mcp_toggle'),
|
||||
serverName: z.string(),
|
||||
enabled: z.boolean(),
|
||||
})
|
||||
.describe('Enables or disables an MCP server.'),
|
||||
)
|
||||
|
||||
|
||||
export const SDKControlStopTaskRequestSchema = lazySchema(() =>
|
||||
z
|
||||
.object({
|
||||
subtype: z.literal('stop_task'),
|
||||
task_id: z.string(),
|
||||
})
|
||||
.describe('Stops a running task.'),
|
||||
)
|
||||
|
||||
export const SDKControlApplyFlagSettingsRequestSchema = lazySchema(() =>
|
||||
z
|
||||
.object({
|
||||
subtype: z.literal('apply_flag_settings'),
|
||||
settings: z.record(z.string(), z.unknown()),
|
||||
})
|
||||
.describe(
|
||||
'Merges the provided settings into the flag settings layer, updating the active configuration.',
|
||||
),
|
||||
)
|
||||
|
||||
export const SDKControlGetSettingsRequestSchema = lazySchema(() =>
|
||||
z
|
||||
.object({
|
||||
subtype: z.literal('get_settings'),
|
||||
})
|
||||
.describe(
|
||||
'Returns the effective merged settings and the raw per-source settings.',
|
||||
),
|
||||
)
|
||||
|
||||
export const SDKControlGetSettingsResponseSchema = lazySchema(() =>
|
||||
z
|
||||
.object({
|
||||
effective: z.record(z.string(), z.unknown()),
|
||||
sources: z
|
||||
.array(
|
||||
z.object({
|
||||
source: z.enum([
|
||||
'userSettings',
|
||||
'projectSettings',
|
||||
'localSettings',
|
||||
'flagSettings',
|
||||
'policySettings',
|
||||
]),
|
||||
settings: z.record(z.string(), z.unknown()),
|
||||
}),
|
||||
)
|
||||
.describe(
|
||||
'Ordered low-to-high priority — later entries override earlier ones.',
|
||||
),
|
||||
applied: z
|
||||
.object({
|
||||
model: z.string(),
|
||||
// String levels only — numeric effort is ant-only and the
|
||||
// Zod→proto generator can't emit enum∪number unions.
|
||||
effort: z.enum(['low', 'medium', 'high', 'max']).nullable(),
|
||||
})
|
||||
.optional()
|
||||
.describe(
|
||||
'Runtime-resolved values after env overrides, session state, and model-specific defaults are applied. Unlike `effective` (disk merge), these reflect what will actually be sent to the API.',
|
||||
),
|
||||
})
|
||||
.describe(
|
||||
'Effective merged settings plus raw per-source settings in merge order.',
|
||||
),
|
||||
)
|
||||
|
||||
export const SDKControlElicitationRequestSchema = lazySchema(() =>
|
||||
z
|
||||
.object({
|
||||
subtype: z.literal('elicitation'),
|
||||
mcp_server_name: z.string(),
|
||||
message: z.string(),
|
||||
mode: z.enum(['form', 'url']).optional(),
|
||||
url: z.string().optional(),
|
||||
elicitation_id: z.string().optional(),
|
||||
requested_schema: z.record(z.string(), z.unknown()).optional(),
|
||||
})
|
||||
.describe(
|
||||
'Requests the SDK consumer to handle an MCP elicitation (user input request).',
|
||||
),
|
||||
)
|
||||
|
||||
export const SDKControlElicitationResponseSchema = lazySchema(() =>
|
||||
z
|
||||
.object({
|
||||
action: z.enum(['accept', 'decline', 'cancel']),
|
||||
content: z.record(z.string(), z.unknown()).optional(),
|
||||
})
|
||||
.describe('Response from the SDK consumer for an elicitation request.'),
|
||||
)
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// Control Request/Response Wrappers
|
||||
// ============================================================================
|
||||
|
||||
export const SDKControlRequestInnerSchema = lazySchema(() =>
|
||||
z.union([
|
||||
SDKControlInterruptRequestSchema(),
|
||||
SDKControlPermissionRequestSchema(),
|
||||
SDKControlInitializeRequestSchema(),
|
||||
SDKControlSetPermissionModeRequestSchema(),
|
||||
SDKControlSetModelRequestSchema(),
|
||||
SDKControlSetMaxThinkingTokensRequestSchema(),
|
||||
SDKControlMcpStatusRequestSchema(),
|
||||
SDKControlGetContextUsageRequestSchema(),
|
||||
SDKHookCallbackRequestSchema(),
|
||||
SDKControlMcpMessageRequestSchema(),
|
||||
SDKControlRewindFilesRequestSchema(),
|
||||
SDKControlCancelAsyncMessageRequestSchema(),
|
||||
SDKControlSeedReadStateRequestSchema(),
|
||||
SDKControlMcpSetServersRequestSchema(),
|
||||
SDKControlReloadPluginsRequestSchema(),
|
||||
SDKControlMcpReconnectRequestSchema(),
|
||||
SDKControlMcpToggleRequestSchema(),
|
||||
SDKControlStopTaskRequestSchema(),
|
||||
SDKControlApplyFlagSettingsRequestSchema(),
|
||||
SDKControlGetSettingsRequestSchema(),
|
||||
SDKControlElicitationRequestSchema(),
|
||||
]),
|
||||
)
|
||||
|
||||
export const SDKControlRequestSchema = lazySchema(() =>
|
||||
z.object({
|
||||
type: z.literal('control_request'),
|
||||
request_id: z.string(),
|
||||
request: SDKControlRequestInnerSchema(),
|
||||
}),
|
||||
)
|
||||
|
||||
export const ControlResponseSchema = lazySchema(() =>
|
||||
z.object({
|
||||
subtype: z.literal('success'),
|
||||
request_id: z.string(),
|
||||
response: z.record(z.string(), z.unknown()).optional(),
|
||||
}),
|
||||
)
|
||||
|
||||
export const ControlErrorResponseSchema = lazySchema(() =>
|
||||
z.object({
|
||||
subtype: z.literal('error'),
|
||||
request_id: z.string(),
|
||||
error: z.string(),
|
||||
pending_permission_requests: z
|
||||
.array(z.lazy(() => SDKControlRequestSchema()))
|
||||
.optional(),
|
||||
}),
|
||||
)
|
||||
|
||||
export const SDKControlResponseSchema = lazySchema(() =>
|
||||
z.object({
|
||||
type: z.literal('control_response'),
|
||||
response: z.union([ControlResponseSchema(), ControlErrorResponseSchema()]),
|
||||
}),
|
||||
)
|
||||
|
||||
export const SDKControlCancelRequestSchema = lazySchema(() =>
|
||||
z
|
||||
.object({
|
||||
type: z.literal('control_cancel_request'),
|
||||
request_id: z.string(),
|
||||
})
|
||||
.describe('Cancels a currently open control request.'),
|
||||
)
|
||||
|
||||
export const SDKKeepAliveMessageSchema = lazySchema(() =>
|
||||
z
|
||||
.object({
|
||||
type: z.literal('keep_alive'),
|
||||
})
|
||||
.describe('Keep-alive message to maintain WebSocket connection.'),
|
||||
)
|
||||
|
||||
export const SDKUpdateEnvironmentVariablesMessageSchema = lazySchema(() =>
|
||||
z
|
||||
.object({
|
||||
type: z.literal('update_environment_variables'),
|
||||
variables: z.record(z.string(), z.string()),
|
||||
})
|
||||
.describe('Updates environment variables at runtime.'),
|
||||
)
|
||||
|
||||
// ============================================================================
|
||||
// Aggregate Message Types
|
||||
// ============================================================================
|
||||
|
||||
export const StdoutMessageSchema = lazySchema(() =>
|
||||
z.union([
|
||||
SDKMessageSchema(),
|
||||
SDKStreamlinedTextMessageSchema(),
|
||||
SDKStreamlinedToolUseSummaryMessageSchema(),
|
||||
SDKPostTurnSummaryMessageSchema(),
|
||||
SDKControlResponseSchema(),
|
||||
SDKControlRequestSchema(),
|
||||
SDKControlCancelRequestSchema(),
|
||||
SDKKeepAliveMessageSchema(),
|
||||
]),
|
||||
)
|
||||
|
||||
export const StdinMessageSchema = lazySchema(() =>
|
||||
z.union([
|
||||
SDKUserMessageSchema(),
|
||||
SDKControlRequestSchema(),
|
||||
SDKControlResponseSchema(),
|
||||
SDKKeepAliveMessageSchema(),
|
||||
SDKUpdateEnvironmentVariablesMessageSchema(),
|
||||
]),
|
||||
)
|
||||
1889
src/entrypoints/sdk/coreSchemas.ts
Normal file
1889
src/entrypoints/sdk/coreSchemas.ts
Normal file
File diff suppressed because it is too large
Load Diff
62
src/entrypoints/sdk/coreTypes.ts
Normal file
62
src/entrypoints/sdk/coreTypes.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
// SDK Core Types - Common serializable types used by both SDK consumers and SDK builders.
|
||||
//
|
||||
// Types are generated from Zod schemas in coreSchemas.ts.
|
||||
// To modify types:
|
||||
// 1. Edit Zod schemas in coreSchemas.ts
|
||||
// 2. Run: bun scripts/generate-sdk-types.ts
|
||||
//
|
||||
// Schemas are available in coreSchemas.ts for runtime validation but are not
|
||||
// part of the public API.
|
||||
|
||||
// Re-export sandbox types for SDK consumers
|
||||
export type {
|
||||
SandboxFilesystemConfig,
|
||||
SandboxIgnoreViolations,
|
||||
SandboxNetworkConfig,
|
||||
SandboxSettings,
|
||||
} from '../sandboxTypes.js'
|
||||
// Re-export all generated types
|
||||
export * from './coreTypes.generated.js'
|
||||
|
||||
// Re-export utility types that can't be expressed as Zod schemas
|
||||
export type { NonNullableUsage } from './sdkUtilityTypes.js'
|
||||
|
||||
// Const arrays for runtime usage
|
||||
export const HOOK_EVENTS = [
|
||||
'PreToolUse',
|
||||
'PostToolUse',
|
||||
'PostToolUseFailure',
|
||||
'Notification',
|
||||
'UserPromptSubmit',
|
||||
'SessionStart',
|
||||
'SessionEnd',
|
||||
'Stop',
|
||||
'StopFailure',
|
||||
'SubagentStart',
|
||||
'SubagentStop',
|
||||
'PreCompact',
|
||||
'PostCompact',
|
||||
'PermissionRequest',
|
||||
'PermissionDenied',
|
||||
'Setup',
|
||||
'TeammateIdle',
|
||||
'TaskCreated',
|
||||
'TaskCompleted',
|
||||
'Elicitation',
|
||||
'ElicitationResult',
|
||||
'ConfigChange',
|
||||
'WorktreeCreate',
|
||||
'WorktreeRemove',
|
||||
'InstructionsLoaded',
|
||||
'CwdChanged',
|
||||
'FileChanged',
|
||||
] as const
|
||||
|
||||
export const EXIT_REASONS = [
|
||||
'clear',
|
||||
'resume',
|
||||
'logout',
|
||||
'prompt_input_exit',
|
||||
'other',
|
||||
'bypass_permissions_disabled',
|
||||
] as const
|
||||
Reference in New Issue
Block a user