Compare commits
17 Commits
e8da02f5bc
...
codex-remo
| Author | SHA1 | Date | |
|---|---|---|---|
| d04ef7e701 | |||
| 3606a51288 | |||
| 2cf6c23fdc | |||
| 4eb053eef5 | |||
| d7bb164a05 | |||
| 108704ef22 | |||
| 04f612db35 | |||
| 64c2b201be | |||
| 9a4bb0e1ca | |||
| af2f2b0e34 | |||
| 097bc753f0 | |||
| f25dc3b6be | |||
| 9410e2f394 | |||
| 20523234e0 | |||
| ecec8b6ba7 | |||
| 23a61be07b | |||
| fa9c74e7b7 |
@@ -13,7 +13,7 @@ import type {
|
||||
SDKPermissionDenial,
|
||||
SDKStatus,
|
||||
SDKUserMessageReplay,
|
||||
} from 'src/entrypoints/agentSdkTypes.js'
|
||||
} from 'src/entrypoints/agentSdkTypes.ts'
|
||||
import { accumulateUsage, updateUsage } from 'src/services/api/claude.js'
|
||||
import type { NonNullableUsage } from 'src/services/api/logging.js'
|
||||
import { EMPTY_USAGE } from 'src/services/api/logging.js'
|
||||
|
||||
@@ -74,7 +74,7 @@ export type {
|
||||
|
||||
import type { SpinnerMode } from './components/Spinner.js'
|
||||
import type { QuerySource } from './constants/querySource.js'
|
||||
import type { SDKStatus } from './entrypoints/agentSdkTypes.js'
|
||||
import type { SDKStatus } from './entrypoints/agentSdkTypes.ts'
|
||||
import type { AppState } from './state/AppState.js'
|
||||
import type {
|
||||
HookProgress,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import axios from 'axios'
|
||||
import { getOauthConfig } from '../constants/oauth.js'
|
||||
import type { SDKMessage } from '../entrypoints/agentSdkTypes.js'
|
||||
import type { SDKMessage } from '../entrypoints/agentSdkTypes.ts'
|
||||
import { logForDebugging } from '../utils/debug.js'
|
||||
import { getOAuthHeaders, prepareApiRequest } from '../utils/teleport/api.js'
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import type { BasicTracerProvider } from '@opentelemetry/sdk-trace-base'
|
||||
import { realpathSync } from 'fs'
|
||||
import sumBy from 'lodash-es/sumBy.js'
|
||||
import { cwd } from 'process'
|
||||
import type { HookEvent, ModelUsage } from 'src/entrypoints/agentSdkTypes.js'
|
||||
import type { HookEvent, ModelUsage } from 'src/entrypoints/agentSdkTypes.ts'
|
||||
import type { AgentColorName } from 'src/tools/AgentTool/agentColorManager.js'
|
||||
import type { HookCallbackMatcher } from 'src/types/hooks.js'
|
||||
// Indirection for browser-sdk build (package.json "browser" field swaps
|
||||
|
||||
@@ -154,10 +154,6 @@ export function createBridgeApiClient(deps: BridgeApiDeps): BridgeApiClient {
|
||||
}>(
|
||||
`${deps.baseUrl}/v1/environments/bridge`,
|
||||
{
|
||||
machine_name: config.machineName,
|
||||
directory: config.dir,
|
||||
branch: config.branch,
|
||||
git_repo_url: config.gitRepoUrl,
|
||||
// Advertise session capacity so claude.ai/code can show
|
||||
// "2/4 sessions" badges and only block the picker when
|
||||
// actually at capacity. Backends that don't yet accept
|
||||
@@ -190,7 +186,7 @@ export function createBridgeApiClient(deps: BridgeApiDeps): BridgeApiClient {
|
||||
`[bridge:api] POST /v1/environments/bridge -> ${response.status} environment_id=${response.data.environment_id}`,
|
||||
)
|
||||
debug(
|
||||
`[bridge:api] >>> ${debugBody({ machine_name: config.machineName, directory: config.dir, branch: config.branch, git_repo_url: config.gitRepoUrl, max_sessions: config.maxSessions, metadata: { worker_type: config.workerType } })}`,
|
||||
`[bridge:api] >>> ${debugBody({ max_sessions: config.maxSessions, metadata: { worker_type: config.workerType } })}`,
|
||||
)
|
||||
debug(`[bridge:api] <<< ${debugBody(response.data)}`)
|
||||
return response.data
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { feature } from 'bun:bundle'
|
||||
import { randomUUID } from 'crypto'
|
||||
import { hostname, tmpdir } from 'os'
|
||||
import { tmpdir } from 'os'
|
||||
import { basename, join, resolve } from 'path'
|
||||
import { getRemoteSessionUrl } from '../constants/product.js'
|
||||
import { shutdownDatadog } from '../services/analytics/datadog.js'
|
||||
@@ -2203,9 +2203,7 @@ export async function bridgeMain(args: string[]): Promise<void> {
|
||||
? process.env.CLAUDE_BRIDGE_SESSION_INGRESS_URL
|
||||
: baseUrl
|
||||
|
||||
const { getBranch, getRemoteUrl, findGitRoot } = await import(
|
||||
'../utils/git.js'
|
||||
)
|
||||
const { findGitRoot } = await import('../utils/git.js')
|
||||
|
||||
// Precheck worktree availability for the first-run dialog and the `w`
|
||||
// toggle. Unconditional so we know upfront whether worktree is an option.
|
||||
@@ -2337,9 +2335,6 @@ export async function bridgeMain(args: string[]): Promise<void> {
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
const branch = await getBranch()
|
||||
const gitRepoUrl = await getRemoteUrl()
|
||||
const machineName = hostname()
|
||||
const bridgeId = randomUUID()
|
||||
|
||||
const { handleOAuth401Error } = await import('../utils/auth.js')
|
||||
@@ -2417,9 +2412,6 @@ export async function bridgeMain(args: string[]): Promise<void> {
|
||||
|
||||
const config: BridgeConfig = {
|
||||
dir,
|
||||
machineName,
|
||||
branch,
|
||||
gitRepoUrl,
|
||||
maxSessions,
|
||||
spawnMode,
|
||||
verbose,
|
||||
@@ -2435,7 +2427,7 @@ export async function bridgeMain(args: string[]): Promise<void> {
|
||||
}
|
||||
|
||||
logForDebugging(
|
||||
`[bridge:init] bridgeId=${bridgeId}${reuseEnvironmentId ? ` reuseEnvironmentId=${reuseEnvironmentId}` : ''} dir=${dir} branch=${branch} gitRepoUrl=${gitRepoUrl} machine=${machineName}`,
|
||||
`[bridge:init] bridgeId=${bridgeId}${reuseEnvironmentId ? ` reuseEnvironmentId=${reuseEnvironmentId}` : ''} dir=${dir}`,
|
||||
)
|
||||
logForDebugging(
|
||||
`[bridge:init] apiBaseUrl=${baseUrl} sessionIngressUrl=${sessionIngressUrl}`,
|
||||
@@ -2591,11 +2583,7 @@ export async function bridgeMain(args: string[]): Promise<void> {
|
||||
})
|
||||
|
||||
const logger = createBridgeLogger({ verbose })
|
||||
const { parseGitHubRepository } = await import('../utils/detectRepository.js')
|
||||
const ownerRepo = gitRepoUrl ? parseGitHubRepository(gitRepoUrl) : null
|
||||
// Use the repo name from the parsed owner/repo, or fall back to the dir basename
|
||||
const repoName = ownerRepo ? ownerRepo.split('/').pop()! : basename(dir)
|
||||
logger.setRepoInfo(repoName, branch)
|
||||
logger.setRepoInfo(basename(dir), '')
|
||||
|
||||
// `w` toggle is available iff we're in a multi-session mode AND worktree
|
||||
// is a valid option. When unavailable, the mode suffix and hint are hidden.
|
||||
@@ -2678,8 +2666,6 @@ export async function bridgeMain(args: string[]): Promise<void> {
|
||||
environmentId,
|
||||
title: name,
|
||||
events: [],
|
||||
gitRepoUrl,
|
||||
branch,
|
||||
signal: controller.signal,
|
||||
baseUrl,
|
||||
getAccessToken: getBridgeAccessToken,
|
||||
@@ -2856,9 +2842,7 @@ export async function runBridgeHeadless(
|
||||
? process.env.CLAUDE_BRIDGE_SESSION_INGRESS_URL
|
||||
: baseUrl
|
||||
|
||||
const { getBranch, getRemoteUrl, findGitRoot } = await import(
|
||||
'../utils/git.js'
|
||||
)
|
||||
const { findGitRoot } = await import('../utils/git.js')
|
||||
const { hasWorktreeCreateHook } = await import('../utils/hooks.js')
|
||||
|
||||
if (opts.spawnMode === 'worktree') {
|
||||
@@ -2871,16 +2855,10 @@ export async function runBridgeHeadless(
|
||||
}
|
||||
}
|
||||
|
||||
const branch = await getBranch()
|
||||
const gitRepoUrl = await getRemoteUrl()
|
||||
const machineName = hostname()
|
||||
const bridgeId = randomUUID()
|
||||
|
||||
const config: BridgeConfig = {
|
||||
dir,
|
||||
machineName,
|
||||
branch,
|
||||
gitRepoUrl,
|
||||
maxSessions: opts.capacity,
|
||||
spawnMode: opts.spawnMode,
|
||||
verbose: false,
|
||||
@@ -2934,8 +2912,6 @@ export async function runBridgeHeadless(
|
||||
environmentId,
|
||||
title: opts.name,
|
||||
events: [],
|
||||
gitRepoUrl,
|
||||
branch,
|
||||
signal,
|
||||
baseUrl,
|
||||
getAccessToken: opts.getAccessToken,
|
||||
|
||||
@@ -11,12 +11,12 @@
|
||||
*/
|
||||
|
||||
import { randomUUID } from 'crypto'
|
||||
import type { SDKMessage } from '../entrypoints/agentSdkTypes.js'
|
||||
import type { SDKMessage } from '../entrypoints/agentSdkTypes.ts'
|
||||
import type {
|
||||
SDKControlRequest,
|
||||
SDKControlResponse,
|
||||
} from '../entrypoints/sdk/controlTypes.js'
|
||||
import type { SDKResultSuccess } from '../entrypoints/sdk/coreTypes.js'
|
||||
} from '../entrypoints/sdk/controlTypes.ts'
|
||||
import type { SDKResultSuccess } from '../entrypoints/sdk/coreTypes.ts'
|
||||
import { logEvent } from '../services/analytics/index.js'
|
||||
import { EMPTY_USAGE } from '../services/api/emptyUsage.js'
|
||||
import type { Message } from '../types/message.js'
|
||||
|
||||
@@ -1,20 +1,9 @@
|
||||
import type { SDKMessage } from '../entrypoints/agentSdkTypes.js'
|
||||
import type { SDKMessage } from '../entrypoints/agentSdkTypes.ts'
|
||||
import { logForDebugging } from '../utils/debug.js'
|
||||
import { errorMessage } from '../utils/errors.js'
|
||||
import { extractErrorDetail } from './debugUtils.js'
|
||||
import { toCompatSessionId } from './sessionIdCompat.js'
|
||||
|
||||
type GitSource = {
|
||||
type: 'git_repository'
|
||||
url: string
|
||||
revision?: string
|
||||
}
|
||||
|
||||
type GitOutcome = {
|
||||
type: 'git_repository'
|
||||
git_info: { type: 'github'; repo: string; branches: string[] }
|
||||
}
|
||||
|
||||
// Events must be wrapped in { type: 'event', data: <sdk_message> } for the
|
||||
// POST /v1/sessions endpoint (discriminated union format).
|
||||
type SessionEvent = {
|
||||
@@ -35,8 +24,6 @@ export async function createBridgeSession({
|
||||
environmentId,
|
||||
title,
|
||||
events,
|
||||
gitRepoUrl,
|
||||
branch,
|
||||
signal,
|
||||
baseUrl: baseUrlOverride,
|
||||
getAccessToken,
|
||||
@@ -45,8 +32,6 @@ export async function createBridgeSession({
|
||||
environmentId: string
|
||||
title?: string
|
||||
events: SessionEvent[]
|
||||
gitRepoUrl: string | null
|
||||
branch: string
|
||||
signal: AbortSignal
|
||||
baseUrl?: string
|
||||
getAccessToken?: () => string | undefined
|
||||
@@ -56,8 +41,6 @@ export async function createBridgeSession({
|
||||
const { getOrganizationUUID } = await import('../services/oauth/client.js')
|
||||
const { getOauthConfig } = await import('../constants/oauth.js')
|
||||
const { getOAuthHeaders } = await import('../utils/teleport/api.js')
|
||||
const { parseGitHubRepository } = await import('../utils/detectRepository.js')
|
||||
const { getDefaultBranch } = await import('../utils/git.js')
|
||||
const { getMainLoopModel } = await import('../utils/model/model.js')
|
||||
const { default: axios } = await import('axios')
|
||||
|
||||
@@ -74,60 +57,12 @@ export async function createBridgeSession({
|
||||
return null
|
||||
}
|
||||
|
||||
// Build git source and outcome context
|
||||
let gitSource: GitSource | null = null
|
||||
let gitOutcome: GitOutcome | null = null
|
||||
|
||||
if (gitRepoUrl) {
|
||||
const { parseGitRemote } = await import('../utils/detectRepository.js')
|
||||
const parsed = parseGitRemote(gitRepoUrl)
|
||||
if (parsed) {
|
||||
const { host, owner, name } = parsed
|
||||
const revision = branch || (await getDefaultBranch()) || undefined
|
||||
gitSource = {
|
||||
type: 'git_repository',
|
||||
url: `https://${host}/${owner}/${name}`,
|
||||
revision,
|
||||
}
|
||||
gitOutcome = {
|
||||
type: 'git_repository',
|
||||
git_info: {
|
||||
type: 'github',
|
||||
repo: `${owner}/${name}`,
|
||||
branches: [`claude/${branch || 'task'}`],
|
||||
},
|
||||
}
|
||||
} else {
|
||||
// Fallback: try parseGitHubRepository for owner/repo format
|
||||
const ownerRepo = parseGitHubRepository(gitRepoUrl)
|
||||
if (ownerRepo) {
|
||||
const [owner, name] = ownerRepo.split('/')
|
||||
if (owner && name) {
|
||||
const revision = branch || (await getDefaultBranch()) || undefined
|
||||
gitSource = {
|
||||
type: 'git_repository',
|
||||
url: `https://github.com/${owner}/${name}`,
|
||||
revision,
|
||||
}
|
||||
gitOutcome = {
|
||||
type: 'git_repository',
|
||||
git_info: {
|
||||
type: 'github',
|
||||
repo: `${owner}/${name}`,
|
||||
branches: [`claude/${branch || 'task'}`],
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const requestBody = {
|
||||
...(title !== undefined && { title }),
|
||||
events,
|
||||
session_context: {
|
||||
sources: gitSource ? [gitSource] : [],
|
||||
outcomes: gitOutcome ? [gitOutcome] : [],
|
||||
sources: [],
|
||||
outcomes: [],
|
||||
model: getMainLoopModel(),
|
||||
},
|
||||
environment_id: environmentId,
|
||||
|
||||
@@ -4,7 +4,7 @@ import type {
|
||||
ImageBlockParam,
|
||||
} from '@anthropic-ai/sdk/resources/messages.mjs'
|
||||
import type { UUID } from 'crypto'
|
||||
import type { SDKMessage } from '../entrypoints/agentSdkTypes.js'
|
||||
import type { SDKMessage } from '../entrypoints/agentSdkTypes.ts'
|
||||
import { detectImageFormatFromBase64 } from '../utils/imageResizer.js'
|
||||
|
||||
/**
|
||||
|
||||
@@ -14,10 +14,9 @@
|
||||
*/
|
||||
|
||||
import { feature } from 'bun:bundle'
|
||||
import { hostname } from 'os'
|
||||
import { getOriginalCwd, getSessionId } from '../bootstrap/state.js'
|
||||
import type { SDKMessage } from '../entrypoints/agentSdkTypes.js'
|
||||
import type { SDKControlResponse } from '../entrypoints/sdk/controlTypes.js'
|
||||
import type { SDKMessage } from '../entrypoints/agentSdkTypes.ts'
|
||||
import type { SDKControlResponse } from '../entrypoints/sdk/controlTypes.ts'
|
||||
import { getFeatureValue_CACHED_WITH_REFRESH } from '../services/analytics/growthbook.js'
|
||||
import { getOrganizationUUID } from '../services/oauth/client.js'
|
||||
import {
|
||||
@@ -34,7 +33,6 @@ import { getGlobalConfig, saveGlobalConfig } from '../utils/config.js'
|
||||
import { logForDebugging } from '../utils/debug.js'
|
||||
import { stripDisplayTagsAllowEmpty } from '../utils/displayTags.js'
|
||||
import { errorMessage } from '../utils/errors.js'
|
||||
import { getBranch, getRemoteUrl } from '../utils/git.js'
|
||||
import { toSDKMessages } from '../utils/messages/mappers.js'
|
||||
import {
|
||||
getContentText,
|
||||
@@ -460,10 +458,6 @@ export async function initReplBridge(
|
||||
return null
|
||||
}
|
||||
|
||||
// Gather git context — this is the bootstrap-read boundary.
|
||||
// Everything from here down is passed explicitly to bridgeCore.
|
||||
const branch = await getBranch()
|
||||
const gitRepoUrl = await getRemoteUrl()
|
||||
const sessionIngressUrl =
|
||||
process.env.USER_TYPE === 'ant' &&
|
||||
process.env.CLAUDE_BRIDGE_SESSION_INGRESS_URL
|
||||
@@ -489,9 +483,6 @@ export async function initReplBridge(
|
||||
// so no adapter needed — just the narrower type on the way out.
|
||||
return initBridgeCore({
|
||||
dir: getOriginalCwd(),
|
||||
machineName: hostname(),
|
||||
branch,
|
||||
gitRepoUrl,
|
||||
title,
|
||||
baseUrl,
|
||||
sessionIngressUrl,
|
||||
|
||||
@@ -64,11 +64,11 @@ import {
|
||||
} from '../services/analytics/index.js'
|
||||
import type { ReplBridgeHandle, BridgeState } from './replBridge.js'
|
||||
import type { Message } from '../types/message.js'
|
||||
import type { SDKMessage } from '../entrypoints/agentSdkTypes.js'
|
||||
import type { SDKMessage } from '../entrypoints/agentSdkTypes.ts'
|
||||
import type {
|
||||
SDKControlRequest,
|
||||
SDKControlResponse,
|
||||
} from '../entrypoints/sdk/controlTypes.js'
|
||||
} from '../entrypoints/sdk/controlTypes.ts'
|
||||
import type { PermissionMode } from '../utils/permissions/PermissionMode.js'
|
||||
|
||||
const ANTHROPIC_VERSION = '2023-06-01'
|
||||
|
||||
@@ -46,12 +46,12 @@ import {
|
||||
logBridgeSkip,
|
||||
} from './debugUtils.js'
|
||||
import type { Message } from '../types/message.js'
|
||||
import type { SDKMessage } from '../entrypoints/agentSdkTypes.js'
|
||||
import type { SDKMessage } from '../entrypoints/agentSdkTypes.ts'
|
||||
import type { PermissionMode } from '../utils/permissions/PermissionMode.js'
|
||||
import type {
|
||||
SDKControlRequest,
|
||||
SDKControlResponse,
|
||||
} from '../entrypoints/sdk/controlTypes.js'
|
||||
} from '../entrypoints/sdk/controlTypes.ts'
|
||||
import { createCapacityWake, type CapacitySignal } from './capacityWake.js'
|
||||
import { FlushGate } from './flushGate.js'
|
||||
import {
|
||||
@@ -84,15 +84,12 @@ export type BridgeState = 'ready' | 'connected' | 'reconnecting' | 'failed'
|
||||
|
||||
/**
|
||||
* Explicit-param input to initBridgeCore. Everything initReplBridge reads
|
||||
* from bootstrap state (cwd, session ID, git, OAuth) becomes a field here.
|
||||
* from bootstrap state (cwd, session ID, OAuth) becomes a field here.
|
||||
* A daemon caller (Agent SDK, PR 4) that never runs main.tsx fills these
|
||||
* in itself.
|
||||
*/
|
||||
export type BridgeCoreParams = {
|
||||
dir: string
|
||||
machineName: string
|
||||
branch: string
|
||||
gitRepoUrl: string | null
|
||||
title: string
|
||||
baseUrl: string
|
||||
sessionIngressUrl: string
|
||||
@@ -113,14 +110,12 @@ export type BridgeCoreParams = {
|
||||
* Daemon wrapper passes `createBridgeSessionLean` from `sessionApi.ts`
|
||||
* (HTTP-only, orgUUID+model supplied by the daemon caller).
|
||||
*
|
||||
* Receives `gitRepoUrl`+`branch` so the REPL wrapper can build the git
|
||||
* source/outcome for claude.ai's session card. Daemon ignores them.
|
||||
* Receives the registered environment ID and session title. Daemon callers
|
||||
* may supply their own lean session-creation implementation.
|
||||
*/
|
||||
createSession: (opts: {
|
||||
environmentId: string
|
||||
title: string
|
||||
gitRepoUrl: string | null
|
||||
branch: string
|
||||
signal: AbortSignal
|
||||
}) => Promise<string | null>
|
||||
/**
|
||||
@@ -262,9 +257,6 @@ export async function initBridgeCore(
|
||||
): Promise<BridgeCoreHandle | null> {
|
||||
const {
|
||||
dir,
|
||||
machineName,
|
||||
branch,
|
||||
gitRepoUrl,
|
||||
title,
|
||||
baseUrl,
|
||||
sessionIngressUrl,
|
||||
@@ -331,9 +323,6 @@ export async function initBridgeCore(
|
||||
|
||||
const bridgeConfig: BridgeConfig = {
|
||||
dir,
|
||||
machineName,
|
||||
branch,
|
||||
gitRepoUrl,
|
||||
maxSessions: 1,
|
||||
spawnMode: 'single-session',
|
||||
verbose: false,
|
||||
@@ -457,8 +446,6 @@ export async function initBridgeCore(
|
||||
const createdSessionId = await createSession({
|
||||
environmentId,
|
||||
title,
|
||||
gitRepoUrl,
|
||||
branch,
|
||||
signal: AbortSignal.timeout(15_000),
|
||||
})
|
||||
|
||||
@@ -764,8 +751,6 @@ export async function initBridgeCore(
|
||||
const newSessionId = await createSession({
|
||||
environmentId,
|
||||
title: currentTitle,
|
||||
gitRepoUrl,
|
||||
branch,
|
||||
signal: AbortSignal.timeout(15_000),
|
||||
})
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { StdoutMessage } from 'src/entrypoints/sdk/controlTypes.js'
|
||||
import type { StdoutMessage } from 'src/entrypoints/sdk/controlTypes.ts'
|
||||
import { CCRClient } from '../cli/transports/ccrClient.js'
|
||||
import type { HybridTransport } from '../cli/transports/HybridTransport.js'
|
||||
import { SSETransport } from '../cli/transports/SSETransport.js'
|
||||
|
||||
@@ -1,16 +1,7 @@
|
||||
import axios from 'axios'
|
||||
import memoize from 'lodash-es/memoize.js'
|
||||
import { hostname } from 'os'
|
||||
import { getOauthConfig } from '../constants/oauth.js'
|
||||
import {
|
||||
checkGate_CACHED_OR_BLOCKING,
|
||||
getFeatureValue_CACHED_MAY_BE_STALE,
|
||||
} from '../services/analytics/growthbook.js'
|
||||
import { getFeatureValue_CACHED_MAY_BE_STALE } from '../services/analytics/growthbook.js'
|
||||
import { logForDebugging } from '../utils/debug.js'
|
||||
import { errorMessage } from '../utils/errors.js'
|
||||
import { isEssentialTrafficOnly } from '../utils/privacyLevel.js'
|
||||
import { getSecureStorage } from '../utils/secureStorage/index.js'
|
||||
import { jsonStringify } from '../utils/slowOperations.js'
|
||||
|
||||
/**
|
||||
* Trusted device token source for bridge (remote-control) sessions.
|
||||
@@ -38,7 +29,7 @@ function isGateEnabled(): boolean {
|
||||
|
||||
// Memoized — secureStorage.read() spawns a macOS `security` subprocess (~40ms).
|
||||
// bridgeApi.ts calls this from getHeaders() on every poll/heartbeat/ack.
|
||||
// Cache cleared after enrollment (below) and on logout (clearAuthRelatedCaches).
|
||||
// Cache cleared on logout (clearAuthRelatedCaches) and after any local update.
|
||||
//
|
||||
// Only the storage read is memoized — the GrowthBook gate is checked live so
|
||||
// that a gate flip after GrowthBook refresh takes effect without a restart.
|
||||
@@ -64,10 +55,8 @@ export function clearTrustedDeviceTokenCache(): void {
|
||||
|
||||
/**
|
||||
* Clear the stored trusted device token from secure storage and the memo cache.
|
||||
* Called before enrollTrustedDevice() during /login so a stale token from the
|
||||
* previous account isn't sent as X-Trusted-Device-Token while enrollment is
|
||||
* in-flight (enrollTrustedDevice is async — bridge API calls between login and
|
||||
* enrollment completion would otherwise still read the old cached token).
|
||||
* Called during /login so a stale token from the previous account isn't sent
|
||||
* as X-Trusted-Device-Token after account switches.
|
||||
*/
|
||||
export function clearTrustedDeviceToken(): void {
|
||||
if (!isGateEnabled()) {
|
||||
@@ -87,124 +76,11 @@ export function clearTrustedDeviceToken(): void {
|
||||
}
|
||||
|
||||
/**
|
||||
* Enroll this device via POST /auth/trusted_devices and persist the token
|
||||
* to keychain. Best-effort — logs and returns on failure so callers
|
||||
* (post-login hooks) don't block the login flow.
|
||||
*
|
||||
* The server gates enrollment on account_session.created_at < 10min, so
|
||||
* this must be called immediately after a fresh /login. Calling it later
|
||||
* (e.g. lazy enrollment on /bridge 403) will fail with 403 stale_session.
|
||||
* Trusted-device enrollment is disabled in this build. Keep the no-op entry
|
||||
* point so callers can continue to invoke it without branching.
|
||||
*/
|
||||
export async function enrollTrustedDevice(): Promise<void> {
|
||||
try {
|
||||
// checkGate_CACHED_OR_BLOCKING awaits any in-flight GrowthBook re-init
|
||||
// (triggered by refreshGrowthBookAfterAuthChange in login.tsx) before
|
||||
// reading the gate, so we get the post-refresh value.
|
||||
if (!(await checkGate_CACHED_OR_BLOCKING(TRUSTED_DEVICE_GATE))) {
|
||||
logForDebugging(
|
||||
`[trusted-device] Gate ${TRUSTED_DEVICE_GATE} is off, skipping enrollment`,
|
||||
)
|
||||
return
|
||||
}
|
||||
// If CLAUDE_TRUSTED_DEVICE_TOKEN is set (e.g. by an enterprise wrapper),
|
||||
// skip enrollment — the env var takes precedence in readStoredToken() so
|
||||
// any enrolled token would be shadowed and never used.
|
||||
if (process.env.CLAUDE_TRUSTED_DEVICE_TOKEN) {
|
||||
logForDebugging(
|
||||
'[trusted-device] CLAUDE_TRUSTED_DEVICE_TOKEN env var is set, skipping enrollment (env var takes precedence)',
|
||||
)
|
||||
return
|
||||
}
|
||||
// Lazy require — utils/auth.ts transitively pulls ~1300 modules
|
||||
// (config → file → permissions → sessionStorage → commands). Daemon callers
|
||||
// of getTrustedDeviceToken() don't need this; only /login does.
|
||||
/* eslint-disable @typescript-eslint/no-require-imports */
|
||||
const { getClaudeAIOAuthTokens } =
|
||||
require('../utils/auth.js') as typeof import('../utils/auth.js')
|
||||
/* eslint-enable @typescript-eslint/no-require-imports */
|
||||
const accessToken = getClaudeAIOAuthTokens()?.accessToken
|
||||
if (!accessToken) {
|
||||
logForDebugging('[trusted-device] No OAuth token, skipping enrollment')
|
||||
return
|
||||
}
|
||||
// Always re-enroll on /login — the existing token may belong to a
|
||||
// different account (account-switch without /logout). Skipping enrollment
|
||||
// would send the old account's token on the new account's bridge calls.
|
||||
const secureStorage = getSecureStorage()
|
||||
|
||||
if (isEssentialTrafficOnly()) {
|
||||
logForDebugging(
|
||||
'[trusted-device] Essential traffic only, skipping enrollment',
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
const baseUrl = getOauthConfig().BASE_API_URL
|
||||
let response
|
||||
try {
|
||||
response = await axios.post<{
|
||||
device_token?: string
|
||||
device_id?: string
|
||||
}>(
|
||||
`${baseUrl}/api/auth/trusted_devices`,
|
||||
{ display_name: `Claude Code on ${hostname()} · ${process.platform}` },
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
timeout: 10_000,
|
||||
validateStatus: s => s < 500,
|
||||
},
|
||||
)
|
||||
} catch (err: unknown) {
|
||||
logForDebugging(
|
||||
`[trusted-device] Enrollment request failed: ${errorMessage(err)}`,
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
if (response.status !== 200 && response.status !== 201) {
|
||||
logForDebugging(
|
||||
`[trusted-device] Enrollment failed ${response.status}: ${jsonStringify(response.data).slice(0, 200)}`,
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
const token = response.data?.device_token
|
||||
if (!token || typeof token !== 'string') {
|
||||
logForDebugging(
|
||||
'[trusted-device] Enrollment response missing device_token field',
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const storageData = secureStorage.read()
|
||||
if (!storageData) {
|
||||
logForDebugging(
|
||||
'[trusted-device] Cannot read storage, skipping token persist',
|
||||
)
|
||||
return
|
||||
}
|
||||
storageData.trustedDeviceToken = token
|
||||
const result = secureStorage.update(storageData)
|
||||
if (!result.success) {
|
||||
logForDebugging(
|
||||
`[trusted-device] Failed to persist token: ${result.warning ?? 'unknown'}`,
|
||||
)
|
||||
return
|
||||
}
|
||||
readStoredToken.cache?.clear?.()
|
||||
logForDebugging(
|
||||
`[trusted-device] Enrolled device_id=${response.data.device_id ?? 'unknown'}`,
|
||||
)
|
||||
} catch (err: unknown) {
|
||||
logForDebugging(
|
||||
`[trusted-device] Storage write failed: ${errorMessage(err)}`,
|
||||
)
|
||||
}
|
||||
} catch (err: unknown) {
|
||||
logForDebugging(`[trusted-device] Enrollment error: ${errorMessage(err)}`)
|
||||
}
|
||||
logForDebugging(
|
||||
'[trusted-device] Enrollment disabled in this build; skipping trusted device registration',
|
||||
)
|
||||
}
|
||||
|
||||
@@ -80,9 +80,6 @@ export type BridgeWorkerType = 'claude_code' | 'claude_code_assistant'
|
||||
|
||||
export type BridgeConfig = {
|
||||
dir: string
|
||||
machineName: string
|
||||
branch: string
|
||||
gitRepoUrl: string | null
|
||||
maxSessions: number
|
||||
spawnMode: SpawnMode
|
||||
verbose: boolean
|
||||
|
||||
@@ -93,7 +93,7 @@ export function clearSessionCaches(
|
||||
|
||||
// Clear tungsten session usage tracking
|
||||
if (process.env.USER_TYPE === 'ant') {
|
||||
void import('../../tools/TungstenTool/TungstenTool.js').then(
|
||||
void import('../../tools/TungstenTool/TungstenTool.ts').then(
|
||||
({ clearSessionsWithTungstenUsage, resetInitializationState }) => {
|
||||
clearSessionsWithTungstenUsage()
|
||||
resetInitializationState()
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { execFileSync } from 'child_process'
|
||||
import { diffLines } from 'diff'
|
||||
import { constants as fsConstants } from 'fs'
|
||||
import {
|
||||
@@ -2674,7 +2673,7 @@ export type InsightsExport = {
|
||||
|
||||
/**
|
||||
* Build export data from already-computed values.
|
||||
* Used by background upload to S3.
|
||||
* Used by the local report writer.
|
||||
*/
|
||||
export function buildExportData(
|
||||
data: AggregatedData,
|
||||
@@ -3069,35 +3068,8 @@ const usageReport: Command = {
|
||||
{ collectRemote },
|
||||
)
|
||||
|
||||
let reportUrl = `file://${htmlPath}`
|
||||
let uploadHint = ''
|
||||
|
||||
if (process.env.USER_TYPE === 'ant') {
|
||||
// Try to upload to S3
|
||||
const timestamp = new Date()
|
||||
.toISOString()
|
||||
.replace(/[-:]/g, '')
|
||||
.replace('T', '_')
|
||||
.slice(0, 15)
|
||||
const username = process.env.SAFEUSER || process.env.USER || 'unknown'
|
||||
const filename = `${username}_insights_${timestamp}.html`
|
||||
const s3Path = `s3://anthropic-serve/atamkin/cc-user-reports/${filename}`
|
||||
const s3Url = `https://s3-frontend.infra.ant.dev/anthropic-serve/atamkin/cc-user-reports/${filename}`
|
||||
|
||||
reportUrl = s3Url
|
||||
try {
|
||||
execFileSync('ff', ['cp', htmlPath, s3Path], {
|
||||
timeout: 60000,
|
||||
stdio: 'pipe', // Suppress output
|
||||
})
|
||||
} catch {
|
||||
// Upload failed - fall back to local file and show upload command
|
||||
reportUrl = `file://${htmlPath}`
|
||||
uploadHint = `\nAutomatic upload failed. Are you on the boron namespace? Try \`use-bo\` and ensure you've run \`sso\`.
|
||||
To share, run: ff cp ${htmlPath} ${s3Path}
|
||||
Then access at: ${s3Url}`
|
||||
}
|
||||
}
|
||||
const reportUrl = `file://${htmlPath}`
|
||||
const uploadHint = ''
|
||||
|
||||
// Build header with stats
|
||||
const sessionLabel =
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,23 +1,9 @@
|
||||
import axios from 'axios'
|
||||
import { readFile, stat } from 'fs/promises'
|
||||
import type { Message } from '../../types/message.js'
|
||||
import { checkAndRefreshOAuthTokenIfNeeded } from '../../utils/auth.js'
|
||||
import { logForDebugging } from '../../utils/debug.js'
|
||||
import { errorMessage } from '../../utils/errors.js'
|
||||
import { getAuthHeaders, getUserAgent } from '../../utils/http.js'
|
||||
import { normalizeMessagesForAPI } from '../../utils/messages.js'
|
||||
import {
|
||||
extractAgentIdsFromMessages,
|
||||
getTranscriptPath,
|
||||
loadSubagentTranscripts,
|
||||
MAX_TRANSCRIPT_READ_BYTES,
|
||||
} from '../../utils/sessionStorage.js'
|
||||
import { jsonStringify } from '../../utils/slowOperations.js'
|
||||
import { redactSensitiveInfo } from '../Feedback.js'
|
||||
|
||||
type TranscriptShareResult = {
|
||||
success: boolean
|
||||
transcriptId?: string
|
||||
disabled?: boolean
|
||||
}
|
||||
|
||||
export type TranscriptShareTrigger =
|
||||
@@ -27,86 +13,12 @@ export type TranscriptShareTrigger =
|
||||
| 'memory_survey'
|
||||
|
||||
export async function submitTranscriptShare(
|
||||
messages: Message[],
|
||||
trigger: TranscriptShareTrigger,
|
||||
appearanceId: string,
|
||||
_messages: Message[],
|
||||
_trigger: TranscriptShareTrigger,
|
||||
_appearanceId: string,
|
||||
): Promise<TranscriptShareResult> {
|
||||
try {
|
||||
logForDebugging('Collecting transcript for sharing', { level: 'info' })
|
||||
|
||||
const transcript = normalizeMessagesForAPI(messages)
|
||||
|
||||
// Collect subagent transcripts
|
||||
const agentIds = extractAgentIdsFromMessages(messages)
|
||||
const subagentTranscripts = await loadSubagentTranscripts(agentIds)
|
||||
|
||||
// Read raw JSONL transcript (with size guard to prevent OOM)
|
||||
let rawTranscriptJsonl: string | undefined
|
||||
try {
|
||||
const transcriptPath = getTranscriptPath()
|
||||
const { size } = await stat(transcriptPath)
|
||||
if (size <= MAX_TRANSCRIPT_READ_BYTES) {
|
||||
rawTranscriptJsonl = await readFile(transcriptPath, 'utf-8')
|
||||
} else {
|
||||
logForDebugging(
|
||||
`Skipping raw transcript read: file too large (${size} bytes)`,
|
||||
{ level: 'warn' },
|
||||
)
|
||||
}
|
||||
} catch {
|
||||
// File may not exist
|
||||
}
|
||||
|
||||
const data = {
|
||||
trigger,
|
||||
version: MACRO.VERSION,
|
||||
platform: process.platform,
|
||||
transcript,
|
||||
subagentTranscripts:
|
||||
Object.keys(subagentTranscripts).length > 0
|
||||
? subagentTranscripts
|
||||
: undefined,
|
||||
rawTranscriptJsonl,
|
||||
}
|
||||
|
||||
const content = redactSensitiveInfo(jsonStringify(data))
|
||||
|
||||
await checkAndRefreshOAuthTokenIfNeeded()
|
||||
|
||||
const authResult = getAuthHeaders()
|
||||
if (authResult.error) {
|
||||
return { success: false }
|
||||
}
|
||||
|
||||
const headers: Record<string, string> = {
|
||||
'Content-Type': 'application/json',
|
||||
'User-Agent': getUserAgent(),
|
||||
...authResult.headers,
|
||||
}
|
||||
|
||||
const response = await axios.post(
|
||||
'https://api.anthropic.com/api/claude_code_shared_session_transcripts',
|
||||
{ content, appearance_id: appearanceId },
|
||||
{
|
||||
headers,
|
||||
timeout: 30000,
|
||||
},
|
||||
)
|
||||
|
||||
if (response.status === 200 || response.status === 201) {
|
||||
const result = response.data
|
||||
logForDebugging('Transcript shared successfully', { level: 'info' })
|
||||
return {
|
||||
success: true,
|
||||
transcriptId: result?.transcript_id,
|
||||
}
|
||||
}
|
||||
|
||||
return { success: false }
|
||||
} catch (err) {
|
||||
logForDebugging(errorMessage(err), {
|
||||
level: 'error',
|
||||
})
|
||||
return { success: false }
|
||||
return {
|
||||
success: false,
|
||||
disabled: true,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import type { Command } from '../commands.js';
|
||||
import { useTerminalSize } from '../hooks/useTerminalSize.js';
|
||||
import { Box } from '../ink.js';
|
||||
import type { Tools } from '../Tool.js';
|
||||
import { type ConnectorTextBlock, isConnectorTextBlock } from '../types/connectorText.js';
|
||||
import { type ConnectorTextBlock, isConnectorTextBlock } from '../types/connectorText.ts';
|
||||
import type { AssistantMessage, AttachmentMessage as AttachmentMessageType, CollapsedReadSearchGroup as CollapsedReadSearchGroupType, GroupedToolUseMessage as GroupedToolUseMessageType, NormalizedUserMessage, ProgressMessage, SystemMessage } from '../types/message.js';
|
||||
import { type AdvisorBlock, isAdvisorBlock } from '../utils/advisor.js';
|
||||
import { isFullscreenEnvEnabled } from '../utils/fullscreen.js';
|
||||
|
||||
@@ -19,7 +19,7 @@ import { ReadMcpResourceTool } from 'src/tools/ReadMcpResourceTool/ReadMcpResour
|
||||
import { TaskOutputTool } from 'src/tools/TaskOutputTool/TaskOutputTool.js';
|
||||
import { TaskStopTool } from 'src/tools/TaskStopTool/TaskStopTool.js';
|
||||
import { TodoWriteTool } from 'src/tools/TodoWriteTool/TodoWriteTool.js';
|
||||
import { TungstenTool } from 'src/tools/TungstenTool/TungstenTool.js';
|
||||
import { TungstenTool } from 'src/tools/TungstenTool/TungstenTool.ts';
|
||||
import { WebFetchTool } from 'src/tools/WebFetchTool/WebFetchTool.js';
|
||||
import { WebSearchTool } from 'src/tools/WebSearchTool/WebSearchTool.js';
|
||||
import type { KeyboardEvent } from '../../ink/events/keyboard-event.js';
|
||||
|
||||
@@ -13,7 +13,7 @@ import { c as _c } from "react/compiler-runtime";
|
||||
*/
|
||||
import * as React from 'react';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import type { HookEvent } from 'src/entrypoints/agentSdkTypes.js';
|
||||
import type { HookEvent } from 'src/entrypoints/agentSdkTypes.ts';
|
||||
import { useAppState, useAppStateStore } from 'src/state/AppState.js';
|
||||
import type { CommandResultDisplay } from '../../commands.js';
|
||||
import { useSettingsChange } from '../../hooks/useSettingsChange.js';
|
||||
|
||||
@@ -10,7 +10,7 @@ import { c as _c } from "react/compiler-runtime";
|
||||
|
||||
import figures from 'figures';
|
||||
import * as React from 'react';
|
||||
import type { HookEvent } from 'src/entrypoints/agentSdkTypes.js';
|
||||
import type { HookEvent } from 'src/entrypoints/agentSdkTypes.ts';
|
||||
import type { HookEventMetadata } from 'src/utils/hooks/hooksConfigManager.js';
|
||||
import { Box, Link, Text } from '../../ink.js';
|
||||
import { plural } from '../../utils/stringUtils.js';
|
||||
|
||||
@@ -7,7 +7,7 @@ import { c as _c } from "react/compiler-runtime";
|
||||
* confirmation.
|
||||
*/
|
||||
import * as React from 'react';
|
||||
import type { HookEvent } from 'src/entrypoints/agentSdkTypes.js';
|
||||
import type { HookEvent } from 'src/entrypoints/agentSdkTypes.ts';
|
||||
import type { HookEventMetadata } from 'src/utils/hooks/hooksConfigManager.js';
|
||||
import { Box, Text } from '../../ink.js';
|
||||
import { getHookDisplayText, hookSourceHeaderDisplayString, type IndividualHookConfig } from '../../utils/hooks/hooksSettings.js';
|
||||
|
||||
@@ -6,7 +6,7 @@ import { c as _c } from "react/compiler-runtime";
|
||||
* and simply lets the user drill into each matcher to see its hooks.
|
||||
*/
|
||||
import * as React from 'react';
|
||||
import type { HookEvent } from 'src/entrypoints/agentSdkTypes.js';
|
||||
import type { HookEvent } from 'src/entrypoints/agentSdkTypes.ts';
|
||||
import { Box, Text } from '../../ink.js';
|
||||
import { type HookSource, hookSourceInlineDisplayString, type IndividualHookConfig } from '../../utils/hooks/hooksSettings.js';
|
||||
import { plural } from '../../utils/stringUtils.js';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
import * as React from 'react';
|
||||
import type { HookEvent } from 'src/entrypoints/agentSdkTypes.js';
|
||||
import type { HookEvent } from 'src/entrypoints/agentSdkTypes.ts';
|
||||
import type { buildMessageLookups } from 'src/utils/messages.js';
|
||||
import { Box, Text } from '../../ink.js';
|
||||
import { MessageResponse } from '../MessageResponse.js';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
import figures from 'figures';
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import type { SDKMessage } from 'src/entrypoints/agentSdkTypes.js';
|
||||
import type { SDKMessage } from 'src/entrypoints/agentSdkTypes.ts';
|
||||
import type { ToolUseContext } from 'src/Tool.js';
|
||||
import type { DeepImmutable } from 'src/types/utils.js';
|
||||
import type { CommandResultDisplay } from '../../commands.js';
|
||||
|
||||
@@ -448,9 +448,7 @@ export async function getSystemPrompt(
|
||||
mcpClients?: MCPServerConnection[],
|
||||
): Promise<string[]> {
|
||||
if (isEnvTruthy(process.env.CLAUDE_CODE_SIMPLE)) {
|
||||
return [
|
||||
`You are Claude Code, Anthropic's official CLI for Claude.\n\nCWD: ${getCwd()}\nDate: ${getSessionStartDate()}`,
|
||||
]
|
||||
return [`You are Claude Code, Anthropic's official CLI for Claude.`]
|
||||
}
|
||||
|
||||
const cwd = getCwd()
|
||||
@@ -607,8 +605,6 @@ export async function computeEnvInfo(
|
||||
modelId: string,
|
||||
additionalWorkingDirectories?: string[],
|
||||
): Promise<string> {
|
||||
const [isGit, unameSR] = await Promise.all([getIsGit(), getUnameSR()])
|
||||
|
||||
// Undercover: keep ALL model names/IDs out of the system prompt so nothing
|
||||
// internal can leak into public commits/PRs. This includes the public
|
||||
// FRONTIER_MODEL_* constants — if those ever point at an unannounced model,
|
||||
@@ -627,33 +623,20 @@ export async function computeEnvInfo(
|
||||
: `You are powered by the model ${modelId}.`
|
||||
}
|
||||
|
||||
const additionalDirsInfo =
|
||||
additionalWorkingDirectories && additionalWorkingDirectories.length > 0
|
||||
? `Additional working directories: ${additionalWorkingDirectories.join(', ')}\n`
|
||||
: ''
|
||||
|
||||
const cutoff = getKnowledgeCutoff(modelId)
|
||||
const knowledgeCutoffMessage = cutoff
|
||||
? `\n\nAssistant knowledge cutoff is ${cutoff}.`
|
||||
: ''
|
||||
|
||||
return `Here is useful information about the environment you are running in:
|
||||
<env>
|
||||
Working directory: ${getCwd()}
|
||||
Is directory a git repo: ${isGit ? 'Yes' : 'No'}
|
||||
${additionalDirsInfo}Platform: ${env.platform}
|
||||
${getShellInfoLine()}
|
||||
OS Version: ${unameSR}
|
||||
</env>
|
||||
${modelDescription}${knowledgeCutoffMessage}`
|
||||
return [`# Environment`, `You are Claude Code.`, modelDescription, knowledgeCutoffMessage]
|
||||
.filter(Boolean)
|
||||
.join('\n')
|
||||
}
|
||||
|
||||
export async function computeSimpleEnvInfo(
|
||||
modelId: string,
|
||||
additionalWorkingDirectories?: string[],
|
||||
): Promise<string> {
|
||||
const [isGit, unameSR] = await Promise.all([getIsGit(), getUnameSR()])
|
||||
|
||||
// Undercover: strip all model name/ID references. See computeEnvInfo.
|
||||
// DCE: inline the USER_TYPE check at each site — do NOT hoist to a const.
|
||||
let modelDescription: string | null = null
|
||||
@@ -671,42 +654,14 @@ export async function computeSimpleEnvInfo(
|
||||
? `Assistant knowledge cutoff is ${cutoff}.`
|
||||
: null
|
||||
|
||||
const cwd = getCwd()
|
||||
const isWorktree = getCurrentWorktreeSession() !== null
|
||||
|
||||
const envItems = [
|
||||
`Primary working directory: ${cwd}`,
|
||||
isWorktree
|
||||
? `This is a git worktree — an isolated copy of the repository. Run all commands from this directory. Do NOT \`cd\` to the original repository root.`
|
||||
: null,
|
||||
[`Is a git repository: ${isGit}`],
|
||||
additionalWorkingDirectories && additionalWorkingDirectories.length > 0
|
||||
? `Additional working directories:`
|
||||
: null,
|
||||
additionalWorkingDirectories && additionalWorkingDirectories.length > 0
|
||||
? additionalWorkingDirectories
|
||||
: null,
|
||||
`Platform: ${env.platform}`,
|
||||
getShellInfoLine(),
|
||||
`OS Version: ${unameSR}`,
|
||||
modelDescription,
|
||||
knowledgeCutoffMessage,
|
||||
process.env.USER_TYPE === 'ant' && isUndercover()
|
||||
? null
|
||||
: `The most recent Claude model family is Claude 4.5/4.6. Model IDs — Opus 4.6: '${CLAUDE_4_5_OR_4_6_MODEL_IDS.opus}', Sonnet 4.6: '${CLAUDE_4_5_OR_4_6_MODEL_IDS.sonnet}', Haiku 4.5: '${CLAUDE_4_5_OR_4_6_MODEL_IDS.haiku}'. When building AI applications, default to the latest and most capable Claude models.`,
|
||||
process.env.USER_TYPE === 'ant' && isUndercover()
|
||||
? null
|
||||
: `Claude Code is available as a CLI in the terminal, desktop app (Mac/Windows), web app (claude.ai/code), and IDE extensions (VS Code, JetBrains).`,
|
||||
process.env.USER_TYPE === 'ant' && isUndercover()
|
||||
? null
|
||||
: `Fast mode for Claude Code uses the same ${FRONTIER_MODEL_NAME} model with faster output. It does NOT switch to a different model. It can be toggled with /fast.`,
|
||||
].filter(item => item !== null)
|
||||
|
||||
return [
|
||||
`# Environment`,
|
||||
`You have been invoked in the following environment: `,
|
||||
...prependBullets(envItems),
|
||||
].join(`\n`)
|
||||
`You are Claude Code.`,
|
||||
modelDescription,
|
||||
knowledgeCutoffMessage,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(`\n`)
|
||||
}
|
||||
|
||||
// @[MODEL LAUNCH]: Add a knowledge cutoff date for the new model.
|
||||
|
||||
@@ -26,7 +26,7 @@ import { TOOL_SEARCH_TOOL_NAME } from '../tools/ToolSearchTool/prompt.js'
|
||||
import { SYNTHETIC_OUTPUT_TOOL_NAME } from '../tools/SyntheticOutputTool/SyntheticOutputTool.js'
|
||||
import { ENTER_WORKTREE_TOOL_NAME } from '../tools/EnterWorktreeTool/constants.js'
|
||||
import { EXIT_WORKTREE_TOOL_NAME } from '../tools/ExitWorktreeTool/constants.js'
|
||||
import { WORKFLOW_TOOL_NAME } from '../tools/WorkflowTool/constants.js'
|
||||
import { WORKFLOW_TOOL_NAME } from '../tools/WorkflowTool/constants.ts'
|
||||
import {
|
||||
CRON_CREATE_TOOL_NAME,
|
||||
CRON_DELETE_TOOL_NAME,
|
||||
|
||||
179
src/context.ts
179
src/context.ts
@@ -1,25 +1,7 @@
|
||||
import { feature } from 'bun:bundle'
|
||||
import memoize from 'lodash-es/memoize.js'
|
||||
import {
|
||||
getAdditionalDirectoriesForClaudeMd,
|
||||
setCachedClaudeMdContent,
|
||||
} from './bootstrap/state.js'
|
||||
import { getLocalISODate } from './constants/common.js'
|
||||
import {
|
||||
filterInjectedMemoryFiles,
|
||||
getClaudeMds,
|
||||
getMemoryFiles,
|
||||
} from './utils/claudemd.js'
|
||||
import { logForDiagnosticsNoPII } from './utils/diagLogs.js'
|
||||
import { isBareMode, isEnvTruthy } from './utils/envUtils.js'
|
||||
import { execFileNoThrow } from './utils/execFileNoThrow.js'
|
||||
import { getBranch, getDefaultBranch, getIsGit, gitExe } from './utils/git.js'
|
||||
import { shouldIncludeGitInstructions } from './utils/gitSettings.js'
|
||||
import { logError } from './utils/log.js'
|
||||
import { setCachedClaudeMdContent } from './bootstrap/state.js'
|
||||
|
||||
const MAX_STATUS_CHARS = 2000
|
||||
|
||||
// System prompt injection for cache breaking (ant-only, ephemeral debugging state)
|
||||
// System prompt injection remains a local cache-busting hook only.
|
||||
let systemPromptInjection: string | null = null
|
||||
|
||||
export function getSystemPromptInjection(): string | null {
|
||||
@@ -28,162 +10,17 @@ export function getSystemPromptInjection(): string | null {
|
||||
|
||||
export function setSystemPromptInjection(value: string | null): void {
|
||||
systemPromptInjection = value
|
||||
// Clear context caches immediately when injection changes
|
||||
getUserContext.cache.clear?.()
|
||||
getSystemContext.cache.clear?.()
|
||||
}
|
||||
|
||||
export const getGitStatus = memoize(async (): Promise<string | null> => {
|
||||
if (process.env.NODE_ENV === 'test') {
|
||||
// Avoid cycles in tests
|
||||
return null
|
||||
}
|
||||
export const getGitStatus = memoize(async (): Promise<string | null> => null)
|
||||
|
||||
const startTime = Date.now()
|
||||
logForDiagnosticsNoPII('info', 'git_status_started')
|
||||
|
||||
const isGitStart = Date.now()
|
||||
const isGit = await getIsGit()
|
||||
logForDiagnosticsNoPII('info', 'git_is_git_check_completed', {
|
||||
duration_ms: Date.now() - isGitStart,
|
||||
is_git: isGit,
|
||||
})
|
||||
|
||||
if (!isGit) {
|
||||
logForDiagnosticsNoPII('info', 'git_status_skipped_not_git', {
|
||||
duration_ms: Date.now() - startTime,
|
||||
})
|
||||
return null
|
||||
}
|
||||
|
||||
try {
|
||||
const gitCmdsStart = Date.now()
|
||||
const [branch, mainBranch, status, log, userName] = await Promise.all([
|
||||
getBranch(),
|
||||
getDefaultBranch(),
|
||||
execFileNoThrow(gitExe(), ['--no-optional-locks', 'status', '--short'], {
|
||||
preserveOutputOnError: false,
|
||||
}).then(({ stdout }) => stdout.trim()),
|
||||
execFileNoThrow(
|
||||
gitExe(),
|
||||
['--no-optional-locks', 'log', '--oneline', '-n', '5'],
|
||||
{
|
||||
preserveOutputOnError: false,
|
||||
},
|
||||
).then(({ stdout }) => stdout.trim()),
|
||||
execFileNoThrow(gitExe(), ['config', 'user.name'], {
|
||||
preserveOutputOnError: false,
|
||||
}).then(({ stdout }) => stdout.trim()),
|
||||
])
|
||||
|
||||
logForDiagnosticsNoPII('info', 'git_commands_completed', {
|
||||
duration_ms: Date.now() - gitCmdsStart,
|
||||
status_length: status.length,
|
||||
})
|
||||
|
||||
// Check if status exceeds character limit
|
||||
const truncatedStatus =
|
||||
status.length > MAX_STATUS_CHARS
|
||||
? status.substring(0, MAX_STATUS_CHARS) +
|
||||
'\n... (truncated because it exceeds 2k characters. If you need more information, run "git status" using BashTool)'
|
||||
: status
|
||||
|
||||
logForDiagnosticsNoPII('info', 'git_status_completed', {
|
||||
duration_ms: Date.now() - startTime,
|
||||
truncated: status.length > MAX_STATUS_CHARS,
|
||||
})
|
||||
|
||||
return [
|
||||
`This is the git status at the start of the conversation. Note that this status is a snapshot in time, and will not update during the conversation.`,
|
||||
`Current branch: ${branch}`,
|
||||
`Main branch (you will usually use this for PRs): ${mainBranch}`,
|
||||
...(userName ? [`Git user: ${userName}`] : []),
|
||||
`Status:\n${truncatedStatus || '(clean)'}`,
|
||||
`Recent commits:\n${log}`,
|
||||
].join('\n\n')
|
||||
} catch (error) {
|
||||
logForDiagnosticsNoPII('error', 'git_status_failed', {
|
||||
duration_ms: Date.now() - startTime,
|
||||
})
|
||||
logError(error)
|
||||
return null
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* This context is prepended to each conversation, and cached for the duration of the conversation.
|
||||
*/
|
||||
export const getSystemContext = memoize(
|
||||
async (): Promise<{
|
||||
[k: string]: string
|
||||
}> => {
|
||||
const startTime = Date.now()
|
||||
logForDiagnosticsNoPII('info', 'system_context_started')
|
||||
|
||||
// Skip git status in CCR (unnecessary overhead on resume) or when git instructions are disabled
|
||||
const gitStatus =
|
||||
isEnvTruthy(process.env.CLAUDE_CODE_REMOTE) ||
|
||||
!shouldIncludeGitInstructions()
|
||||
? null
|
||||
: await getGitStatus()
|
||||
|
||||
// Include system prompt injection if set (for cache breaking, ant-only)
|
||||
const injection = feature('BREAK_CACHE_COMMAND')
|
||||
? getSystemPromptInjection()
|
||||
: null
|
||||
|
||||
logForDiagnosticsNoPII('info', 'system_context_completed', {
|
||||
duration_ms: Date.now() - startTime,
|
||||
has_git_status: gitStatus !== null,
|
||||
has_injection: injection !== null,
|
||||
})
|
||||
|
||||
return {
|
||||
...(gitStatus && { gitStatus }),
|
||||
...(feature('BREAK_CACHE_COMMAND') && injection
|
||||
? {
|
||||
cacheBreaker: `[CACHE_BREAKER: ${injection}]`,
|
||||
}
|
||||
: {}),
|
||||
}
|
||||
},
|
||||
async (): Promise<Record<string, string>> => ({}),
|
||||
)
|
||||
|
||||
/**
|
||||
* This context is prepended to each conversation, and cached for the duration of the conversation.
|
||||
*/
|
||||
export const getUserContext = memoize(
|
||||
async (): Promise<{
|
||||
[k: string]: string
|
||||
}> => {
|
||||
const startTime = Date.now()
|
||||
logForDiagnosticsNoPII('info', 'user_context_started')
|
||||
|
||||
// CLAUDE_CODE_DISABLE_CLAUDE_MDS: hard off, always.
|
||||
// --bare: skip auto-discovery (cwd walk), BUT honor explicit --add-dir.
|
||||
// --bare means "skip what I didn't ask for", not "ignore what I asked for".
|
||||
const shouldDisableClaudeMd =
|
||||
isEnvTruthy(process.env.CLAUDE_CODE_DISABLE_CLAUDE_MDS) ||
|
||||
(isBareMode() && getAdditionalDirectoriesForClaudeMd().length === 0)
|
||||
// Await the async I/O (readFile/readdir directory walk) so the event
|
||||
// loop yields naturally at the first fs.readFile.
|
||||
const claudeMd = shouldDisableClaudeMd
|
||||
? null
|
||||
: getClaudeMds(filterInjectedMemoryFiles(await getMemoryFiles()))
|
||||
// Cache for the auto-mode classifier (yoloClassifier.ts reads this
|
||||
// instead of importing claudemd.ts directly, which would create a
|
||||
// cycle through permissions/filesystem → permissions → yoloClassifier).
|
||||
setCachedClaudeMdContent(claudeMd || null)
|
||||
|
||||
logForDiagnosticsNoPII('info', 'user_context_completed', {
|
||||
duration_ms: Date.now() - startTime,
|
||||
claudemd_length: claudeMd?.length ?? 0,
|
||||
claudemd_disabled: Boolean(shouldDisableClaudeMd),
|
||||
})
|
||||
|
||||
return {
|
||||
...(claudeMd && { claudeMd }),
|
||||
currentDate: `Today's date is ${getLocalISODate()}.`,
|
||||
}
|
||||
},
|
||||
)
|
||||
export const getUserContext = memoize(async (): Promise<Record<string, string>> => {
|
||||
setCachedClaudeMdContent(null)
|
||||
return {}
|
||||
})
|
||||
|
||||
@@ -27,7 +27,7 @@ import {
|
||||
setCostStateForRestore,
|
||||
setHasUnknownModelCost,
|
||||
} from './bootstrap/state.js'
|
||||
import type { ModelUsage } from './entrypoints/agentSdkTypes.js'
|
||||
import type { ModelUsage } from './entrypoints/agentSdkTypes.ts'
|
||||
import {
|
||||
type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
|
||||
logEvent,
|
||||
|
||||
@@ -19,16 +19,16 @@ import type {
|
||||
export type {
|
||||
SDKControlRequest,
|
||||
SDKControlResponse,
|
||||
} from './sdk/controlTypes.js'
|
||||
} from './sdk/controlTypes.ts'
|
||||
// Re-export core types (common serializable types)
|
||||
export * from './sdk/coreTypes.js'
|
||||
export * from './sdk/coreTypes.ts'
|
||||
// Re-export runtime types (callbacks, interfaces with methods)
|
||||
export * from './sdk/runtimeTypes.js'
|
||||
export * from './sdk/runtimeTypes.ts'
|
||||
|
||||
// Re-export settings types (generated from settings JSON schema)
|
||||
export type { Settings } from './sdk/settingsTypes.generated.js'
|
||||
export type { Settings } from './sdk/settingsTypes.generated.ts'
|
||||
// Re-export tool types (all marked @internal until SDK API stabilizes)
|
||||
export * from './sdk/toolTypes.js'
|
||||
export * from './sdk/toolTypes.ts'
|
||||
|
||||
// ============================================================================
|
||||
// Functions
|
||||
@@ -39,7 +39,7 @@ import type {
|
||||
SDKResultMessage,
|
||||
SDKSessionInfo,
|
||||
SDKUserMessage,
|
||||
} from './sdk/coreTypes.js'
|
||||
} from './sdk/coreTypes.ts'
|
||||
// Import types needed for function signatures
|
||||
import type {
|
||||
AnyZodRawShape,
|
||||
@@ -59,7 +59,7 @@ import type {
|
||||
SdkMcpToolDefinition,
|
||||
SessionMessage,
|
||||
SessionMutationOptions,
|
||||
} from './sdk/runtimeTypes.js'
|
||||
} from './sdk/runtimeTypes.ts'
|
||||
|
||||
export type {
|
||||
ListSessionsOptions,
|
||||
|
||||
@@ -1,16 +1,24 @@
|
||||
import { feature } from 'bun:bundle';
|
||||
|
||||
const CLI_MACRO =
|
||||
typeof MACRO !== 'undefined'
|
||||
? MACRO
|
||||
: {
|
||||
VERSION: 'dev',
|
||||
BUILD_TIME: '',
|
||||
PACKAGE_URL: '@anthropic-ai/claude-code',
|
||||
ISSUES_EXPLAINER:
|
||||
'https://docs.anthropic.com/en/docs/claude-code/feedback',
|
||||
FEEDBACK_CHANNEL: 'github',
|
||||
};
|
||||
// Define MACRO global for development (normally injected by bun build --define)
|
||||
if (typeof MACRO === 'undefined') {
|
||||
(globalThis as typeof globalThis & {
|
||||
MACRO: {
|
||||
VERSION: string
|
||||
BUILD_TIME: string
|
||||
PACKAGE_URL: string
|
||||
ISSUES_EXPLAINER: string
|
||||
FEEDBACK_CHANNEL: string
|
||||
}
|
||||
}).MACRO = {
|
||||
VERSION: '2.1.88-dev',
|
||||
BUILD_TIME: new Date().toISOString(),
|
||||
PACKAGE_URL: 'claude-code-recover',
|
||||
ISSUES_EXPLAINER:
|
||||
'https://docs.anthropic.com/en/docs/claude-code/feedback',
|
||||
FEEDBACK_CHANNEL: 'github',
|
||||
};
|
||||
}
|
||||
|
||||
// Bugfix for corepack auto-pinning, which adds yarnpkg to peoples' package.jsons
|
||||
// eslint-disable-next-line custom-rules/no-top-level-side-effects
|
||||
@@ -49,7 +57,7 @@ async function main(): Promise<void> {
|
||||
if (args.length === 1 && (args[0] === '--version' || args[0] === '-v' || args[0] === '-V')) {
|
||||
// MACRO.VERSION is inlined at build time
|
||||
// biome-ignore lint/suspicious/noConsole:: intentional console output
|
||||
console.log(`${CLI_MACRO.VERSION} (Claude Code)`);
|
||||
console.log(`${MACRO.VERSION} (Claude Code)`);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
export class SDKControlRequest {}
|
||||
export class SDKControlResponse {}
|
||||
325
src/entrypoints/sdk/controlTypes.ts
Normal file
325
src/entrypoints/sdk/controlTypes.ts
Normal file
@@ -0,0 +1,325 @@
|
||||
import type { SDKMessage } from './coreTypes.ts'
|
||||
|
||||
export type SDKPermissionResponse =
|
||||
| {
|
||||
behavior: 'allow'
|
||||
updatedInput?: Record<string, unknown>
|
||||
message?: string
|
||||
}
|
||||
| {
|
||||
behavior: 'deny' | 'ask'
|
||||
message?: string
|
||||
updatedInput?: Record<string, unknown>
|
||||
}
|
||||
|
||||
export type SDKControlInterruptRequest = {
|
||||
subtype: 'interrupt'
|
||||
}
|
||||
|
||||
export type SDKControlPermissionRequest = {
|
||||
subtype: 'can_use_tool'
|
||||
tool_name: string
|
||||
input: Record<string, unknown>
|
||||
permission_suggestions?: Array<Record<string, unknown>>
|
||||
blocked_path?: string
|
||||
decision_reason?: string
|
||||
title?: string
|
||||
display_name?: string
|
||||
tool_use_id: string
|
||||
agent_id?: string
|
||||
description?: string
|
||||
}
|
||||
|
||||
export type SDKControlInitializeRequest = {
|
||||
subtype: 'initialize'
|
||||
hooks?: Record<string, Array<Record<string, unknown>>>
|
||||
sdkMcpServers?: string[]
|
||||
jsonSchema?: Record<string, unknown>
|
||||
systemPrompt?: string
|
||||
appendSystemPrompt?: string
|
||||
agents?: Record<string, unknown>
|
||||
promptSuggestions?: boolean
|
||||
agentProgressSummaries?: boolean
|
||||
}
|
||||
|
||||
export type SDKControlSetPermissionModeRequest = {
|
||||
subtype: 'set_permission_mode'
|
||||
mode: string
|
||||
ultraplan?: boolean
|
||||
}
|
||||
|
||||
export type SDKControlSetModelRequest = {
|
||||
subtype: 'set_model'
|
||||
model?: string
|
||||
}
|
||||
|
||||
export type SDKControlSetMaxThinkingTokensRequest = {
|
||||
subtype: 'set_max_thinking_tokens'
|
||||
max_thinking_tokens: number | null
|
||||
}
|
||||
|
||||
export type SDKControlMcpStatusRequest = {
|
||||
subtype: 'mcp_status'
|
||||
}
|
||||
|
||||
export type SDKControlGetContextUsageRequest = {
|
||||
subtype: 'get_context_usage'
|
||||
}
|
||||
|
||||
export type SDKControlRewindFilesRequest = {
|
||||
subtype: 'rewind_files'
|
||||
paths?: string[]
|
||||
}
|
||||
|
||||
export type SDKControlCancelAsyncMessageRequest = {
|
||||
subtype: 'cancel_async_message'
|
||||
message_uuid: string
|
||||
}
|
||||
|
||||
export type SDKControlSeedReadStateRequest = {
|
||||
subtype: 'seed_read_state'
|
||||
path: string
|
||||
mtime: number
|
||||
}
|
||||
|
||||
export type SDKHookCallbackRequest = {
|
||||
subtype: 'hook_callback'
|
||||
callback_id: string
|
||||
input: Record<string, unknown>
|
||||
tool_use_id?: string
|
||||
}
|
||||
|
||||
export type SDKControlMcpMessageRequest = {
|
||||
subtype: 'mcp_message'
|
||||
server_name: string
|
||||
message: Record<string, unknown>
|
||||
}
|
||||
|
||||
export type SDKControlMcpSetServersRequest = {
|
||||
subtype: 'mcp_set_servers'
|
||||
servers: Record<string, Record<string, unknown>>
|
||||
}
|
||||
|
||||
export type SDKControlReloadPluginsRequest = {
|
||||
subtype: 'reload_plugins'
|
||||
}
|
||||
|
||||
export type SDKControlMcpReconnectRequest = {
|
||||
subtype: 'mcp_reconnect'
|
||||
serverName: string
|
||||
}
|
||||
|
||||
export type SDKControlMcpToggleRequest = {
|
||||
subtype: 'mcp_toggle'
|
||||
serverName: string
|
||||
enabled: boolean
|
||||
}
|
||||
|
||||
export type SDKControlStopTaskRequest = {
|
||||
subtype: 'stop_task'
|
||||
task_id: string
|
||||
}
|
||||
|
||||
export type SDKControlApplyFlagSettingsRequest = {
|
||||
subtype: 'apply_flag_settings'
|
||||
settings?: Record<string, unknown>
|
||||
}
|
||||
|
||||
export type SDKControlGetSettingsRequest = {
|
||||
subtype: 'get_settings'
|
||||
}
|
||||
|
||||
export type SDKControlElicitationRequest = {
|
||||
subtype: 'elicitation'
|
||||
prompt?: string
|
||||
spec?: Record<string, unknown>
|
||||
}
|
||||
|
||||
export type SDKControlRequestInner =
|
||||
| SDKControlInterruptRequest
|
||||
| SDKControlPermissionRequest
|
||||
| SDKControlInitializeRequest
|
||||
| SDKControlSetPermissionModeRequest
|
||||
| SDKControlSetModelRequest
|
||||
| SDKControlSetMaxThinkingTokensRequest
|
||||
| SDKControlMcpStatusRequest
|
||||
| SDKControlGetContextUsageRequest
|
||||
| SDKHookCallbackRequest
|
||||
| SDKControlRewindFilesRequest
|
||||
| SDKControlCancelAsyncMessageRequest
|
||||
| SDKControlSeedReadStateRequest
|
||||
| SDKControlMcpMessageRequest
|
||||
| SDKControlMcpSetServersRequest
|
||||
| SDKControlReloadPluginsRequest
|
||||
| SDKControlMcpReconnectRequest
|
||||
| SDKControlMcpToggleRequest
|
||||
| SDKControlStopTaskRequest
|
||||
| SDKControlApplyFlagSettingsRequest
|
||||
| SDKControlGetSettingsRequest
|
||||
| SDKControlElicitationRequest
|
||||
| ({
|
||||
subtype: string
|
||||
} & Record<string, unknown>)
|
||||
|
||||
export type SDKControlRequest = {
|
||||
type: 'control_request'
|
||||
request_id: string
|
||||
request: SDKControlRequestInner
|
||||
}
|
||||
|
||||
export type SDKControlSuccessResponse = {
|
||||
subtype: 'success'
|
||||
request_id: string
|
||||
response?: SDKPermissionResponse | Record<string, unknown>
|
||||
}
|
||||
|
||||
export type SDKControlErrorResponse = {
|
||||
subtype: 'error'
|
||||
request_id: string
|
||||
error: string
|
||||
}
|
||||
|
||||
export type SDKControlInitializeResponse = {
|
||||
subtype: 'initialize'
|
||||
request_id: string
|
||||
commands?: Array<Record<string, unknown>>
|
||||
agents?: Array<Record<string, unknown>>
|
||||
output_style?: string
|
||||
available_output_styles?: string[]
|
||||
models?: Array<Record<string, unknown>>
|
||||
account?: Record<string, unknown>
|
||||
pid?: number
|
||||
fast_mode_state?: Record<string, unknown>
|
||||
}
|
||||
|
||||
export type SDKControlMcpStatusResponse = {
|
||||
subtype: 'mcp_status'
|
||||
request_id: string
|
||||
mcpServers?: Array<Record<string, unknown>>
|
||||
}
|
||||
|
||||
export type SDKControlCancelAsyncMessageResponse = {
|
||||
subtype: 'cancel_async_message'
|
||||
request_id: string
|
||||
cancelled: boolean
|
||||
}
|
||||
|
||||
export type SDKControlMcpSetServersResponse = {
|
||||
subtype: 'mcp_set_servers'
|
||||
request_id: string
|
||||
added: string[]
|
||||
removed: string[]
|
||||
errors: Record<string, string>
|
||||
}
|
||||
|
||||
export type SDKControlReloadPluginsResponse = {
|
||||
subtype: 'reload_plugins'
|
||||
request_id: string
|
||||
commands?: Array<Record<string, unknown>>
|
||||
agents?: Array<Record<string, unknown>>
|
||||
plugins?: Array<Record<string, unknown>>
|
||||
mcpServers?: Array<Record<string, unknown>>
|
||||
error_count?: number
|
||||
}
|
||||
|
||||
export type SDKControlGetContextUsageResponse = {
|
||||
subtype: 'get_context_usage'
|
||||
request_id: string
|
||||
categories?: Array<Record<string, unknown>>
|
||||
totalTokens?: number
|
||||
maxTokens?: number
|
||||
rawMaxTokens?: number
|
||||
percentage?: number
|
||||
gridRows?: Array<Array<Record<string, unknown>>>
|
||||
model?: string
|
||||
memoryFiles?: Array<Record<string, unknown>>
|
||||
}
|
||||
|
||||
export type SDKControlGetSettingsResponse = {
|
||||
subtype: 'get_settings'
|
||||
request_id: string
|
||||
effective: Record<string, unknown>
|
||||
sources: Array<{
|
||||
source:
|
||||
| 'userSettings'
|
||||
| 'projectSettings'
|
||||
| 'localSettings'
|
||||
| 'flagSettings'
|
||||
| 'policySettings'
|
||||
settings: Record<string, unknown>
|
||||
}>
|
||||
applied?: {
|
||||
model: string
|
||||
effort: 'low' | 'medium' | 'high' | 'max' | null
|
||||
}
|
||||
}
|
||||
|
||||
export type SDKControlElicitationResponse = {
|
||||
subtype: 'elicitation'
|
||||
request_id: string
|
||||
response?: {
|
||||
action?: 'accept' | 'decline' | 'cancel'
|
||||
content?: Record<string, unknown>
|
||||
}
|
||||
}
|
||||
|
||||
export type SDKControlResponseInner =
|
||||
| SDKControlSuccessResponse
|
||||
| SDKControlErrorResponse
|
||||
| SDKControlInitializeResponse
|
||||
| SDKControlMcpStatusResponse
|
||||
| SDKControlCancelAsyncMessageResponse
|
||||
| SDKControlMcpSetServersResponse
|
||||
| SDKControlReloadPluginsResponse
|
||||
| SDKControlGetContextUsageResponse
|
||||
| SDKControlGetSettingsResponse
|
||||
| SDKControlElicitationResponse
|
||||
| ({
|
||||
subtype: string
|
||||
request_id: string
|
||||
} & Record<string, unknown>)
|
||||
|
||||
export type SDKControlResponse = {
|
||||
type: 'control_response'
|
||||
response: SDKControlResponseInner
|
||||
}
|
||||
|
||||
export type SDKControlCancelRequest = {
|
||||
type: 'control_cancel_request'
|
||||
request_id: string
|
||||
tool_use_id?: string
|
||||
}
|
||||
|
||||
export type KeepAliveMessage = {
|
||||
type: 'keep_alive'
|
||||
[key: string]: unknown
|
||||
}
|
||||
|
||||
export type SDKUpdateEnvironmentVariablesMessage = {
|
||||
type: 'update_environment_variables'
|
||||
variables: Record<string, string>
|
||||
}
|
||||
|
||||
export type SDKStreamlinedTextMessage = {
|
||||
type: 'streamlined_text'
|
||||
text: string
|
||||
session_id?: string
|
||||
uuid?: string
|
||||
}
|
||||
|
||||
export type SDKStreamlinedToolUseSummaryMessage = {
|
||||
type: 'streamlined_tool_use_summary'
|
||||
tool_summary: string
|
||||
session_id?: string
|
||||
uuid?: string
|
||||
}
|
||||
|
||||
export type StdoutMessage =
|
||||
| SDKMessage
|
||||
| SDKControlRequest
|
||||
| SDKControlResponse
|
||||
| SDKControlCancelRequest
|
||||
| KeepAliveMessage
|
||||
| SDKUpdateEnvironmentVariablesMessage
|
||||
| SDKStreamlinedTextMessage
|
||||
| SDKStreamlinedToolUseSummaryMessage
|
||||
@@ -1 +0,0 @@
|
||||
export {};
|
||||
@@ -16,7 +16,7 @@ export type {
|
||||
SandboxSettings,
|
||||
} from '../sandboxTypes.js'
|
||||
// Re-export all generated types
|
||||
export * from './coreTypes.generated.js'
|
||||
export * from './coreTypes.generated.ts'
|
||||
|
||||
// Re-export utility types that can't be expressed as Zod schemas
|
||||
export type { NonNullableUsage } from './sdkUtilityTypes.js'
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
export class McpSdkServerConfigWithInstance {}
|
||||
export class SdkMcpToolDefinition {}
|
||||
export class Query {}
|
||||
export class InternalQuery {}
|
||||
export class SDKSession {}
|
||||
export class SDKSessionOptions {}
|
||||
export class InternalOptions {}
|
||||
export class Options {}
|
||||
|
||||
export const AnyZodRawShape = {}
|
||||
export const InferShape = (schema) => schema
|
||||
|
||||
export class ForkSessionOptions {}
|
||||
export class ForkSessionResult {}
|
||||
export class GetSessionInfoOptions {}
|
||||
export class GetSessionMessagesOptions {}
|
||||
export class ListSessionsOptions {}
|
||||
export class SessionMessage {}
|
||||
export class SDKSessionInfo {}
|
||||
export class SDKUserMessage {}
|
||||
export class SDKResultMessage {}
|
||||
export class SessionMutationOptions {}
|
||||
@@ -1,5 +1,11 @@
|
||||
import type { CallToolResult, ToolAnnotations } from '@modelcontextprotocol/sdk/types.js'
|
||||
import type { ZodTypeAny } from 'zod/v4'
|
||||
import type {
|
||||
SDKMessage,
|
||||
SDKResultMessage,
|
||||
SDKSessionInfo as CoreSDKSessionInfo,
|
||||
SDKUserMessage,
|
||||
} from './coreTypes.ts'
|
||||
|
||||
export type EffortLevel = 'low' | 'medium' | 'high' | 'max'
|
||||
|
||||
@@ -19,17 +25,33 @@ export type SdkMcpToolDefinition<Schema extends AnyZodRawShape> = {
|
||||
alwaysLoad?: boolean
|
||||
}
|
||||
|
||||
export type McpSdkServerConfigWithInstance = Record<string, unknown>
|
||||
|
||||
export type Options = Record<string, unknown>
|
||||
export type InternalOptions = Options
|
||||
|
||||
export type SDKSessionOptions = Options & {
|
||||
model?: string
|
||||
export type McpSdkServerConfigWithInstance = Record<string, unknown> & {
|
||||
name?: string
|
||||
version?: string
|
||||
instance?: unknown
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
tools?: Array<SdkMcpToolDefinition<any>>
|
||||
}
|
||||
|
||||
export type Query = AsyncIterable<unknown>
|
||||
export type InternalQuery = AsyncIterable<unknown>
|
||||
export type Options = Record<string, unknown> & {
|
||||
cwd?: string
|
||||
model?: string
|
||||
permissionMode?: string
|
||||
maxThinkingTokens?: number | null
|
||||
}
|
||||
|
||||
export type InternalOptions = Options & {
|
||||
systemPrompt?: string
|
||||
appendSystemPrompt?: string
|
||||
}
|
||||
|
||||
export type SDKSessionOptions = Options & {
|
||||
cwd?: string
|
||||
resume?: boolean
|
||||
}
|
||||
|
||||
export type Query = AsyncIterable<SDKMessage>
|
||||
export type InternalQuery = AsyncIterable<SDKMessage>
|
||||
|
||||
export type SessionMutationOptions = {
|
||||
dir?: string
|
||||
@@ -48,7 +70,10 @@ export type GetSessionMessagesOptions = SessionMutationOptions & {
|
||||
includeSystemMessages?: boolean
|
||||
}
|
||||
|
||||
export type ForkSessionOptions = SessionMutationOptions
|
||||
export type ForkSessionOptions = SessionMutationOptions & {
|
||||
upToMessageId?: string
|
||||
title?: string
|
||||
}
|
||||
|
||||
export type ForkSessionResult = {
|
||||
sessionId: string
|
||||
@@ -56,4 +81,13 @@ export type ForkSessionResult = {
|
||||
|
||||
export type SDKSession = {
|
||||
id: string
|
||||
query(
|
||||
prompt: string | AsyncIterable<SDKUserMessage>,
|
||||
options?: Options,
|
||||
): Query
|
||||
interrupt(): void
|
||||
}
|
||||
|
||||
export type SessionMessage = SDKMessage
|
||||
|
||||
export type SDKSessionInfo = CoreSDKSessionInfo
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
export {};
|
||||
13
src/entrypoints/sdk/settingsTypes.generated.ts
Normal file
13
src/entrypoints/sdk/settingsTypes.generated.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
export type SettingsValue =
|
||||
| string
|
||||
| number
|
||||
| boolean
|
||||
| null
|
||||
| SettingsObject
|
||||
| SettingsValue[]
|
||||
|
||||
export type SettingsObject = {
|
||||
[key: string]: SettingsValue
|
||||
}
|
||||
|
||||
export type Settings = SettingsObject
|
||||
@@ -1 +0,0 @@
|
||||
export class SdkMcpToolDefinition {}
|
||||
23
src/entrypoints/sdk/toolTypes.ts
Executable file → Normal file
23
src/entrypoints/sdk/toolTypes.ts
Executable file → Normal file
@@ -1 +1,22 @@
|
||||
export {}
|
||||
import type { CallToolResult, ToolAnnotations } from '@modelcontextprotocol/sdk/types.js'
|
||||
|
||||
import type { AnyZodRawShape, InferShape, SdkMcpToolDefinition } from './runtimeTypes.ts'
|
||||
|
||||
export type { AnyZodRawShape, InferShape, SdkMcpToolDefinition }
|
||||
|
||||
export type SdkMcpToolHandler<Schema extends AnyZodRawShape> = (
|
||||
args: InferShape<Schema>,
|
||||
extra: unknown,
|
||||
) => Promise<CallToolResult>
|
||||
|
||||
export type SdkMcpToolExtras = {
|
||||
annotations?: ToolAnnotations
|
||||
searchHint?: string
|
||||
alwaysLoad?: boolean
|
||||
}
|
||||
|
||||
export type SdkMcpToolDescriptor<Schema extends AnyZodRawShape> = Pick<
|
||||
SdkMcpToolDefinition<Schema>,
|
||||
'name' | 'description' | 'inputSchema' | 'handler'
|
||||
> &
|
||||
SdkMcpToolExtras
|
||||
|
||||
@@ -10,8 +10,8 @@ import type { Command } from '../commands.js';
|
||||
import { getSlashCommandToolSkills, isBridgeSafeCommand } from '../commands.js';
|
||||
import { getRemoteSessionUrl } from '../constants/product.js';
|
||||
import { useNotifications } from '../context/notifications.js';
|
||||
import type { PermissionMode, SDKMessage } from '../entrypoints/agentSdkTypes.js';
|
||||
import type { SDKControlResponse } from '../entrypoints/sdk/controlTypes.js';
|
||||
import type { PermissionMode, SDKMessage } from '../entrypoints/agentSdkTypes.ts';
|
||||
import type { SDKControlResponse } from '../entrypoints/sdk/controlTypes.ts';
|
||||
import { Text } from '../ink.js';
|
||||
import { getFeatureValue_CACHED_MAY_BE_STALE } from '../services/analytics/growthbook.js';
|
||||
import { useAppState, useAppStateStore, useSetAppState } from '../state/AppState.js';
|
||||
|
||||
14
src/main.tsx
14
src/main.tsx
@@ -65,18 +65,6 @@ import { computeInitialTeamContext } from './utils/swarm/reconnection.js';
|
||||
import { initializeWarningHandler } from './utils/warningHandler.js';
|
||||
import { isWorktreeModeEnabled } from './utils/worktreeModeEnabled.js';
|
||||
|
||||
const MAIN_MACRO =
|
||||
typeof MACRO !== 'undefined'
|
||||
? MACRO
|
||||
: {
|
||||
VERSION: 'dev',
|
||||
BUILD_TIME: '',
|
||||
PACKAGE_URL: '@anthropic-ai/claude-code',
|
||||
ISSUES_EXPLAINER:
|
||||
'https://docs.anthropic.com/en/docs/claude-code/feedback',
|
||||
FEEDBACK_CHANNEL: 'github',
|
||||
};
|
||||
|
||||
// Lazy require to avoid circular dependency: teammate.ts -> AppState.tsx -> ... -> main.tsx
|
||||
/* eslint-disable @typescript-eslint/no-require-imports */
|
||||
const getTeammateUtils = () => require('./utils/teammate.js') as typeof import('./utils/teammate.js');
|
||||
@@ -3817,7 +3805,7 @@ async function run(): Promise<CommanderCommand> {
|
||||
pendingHookMessages
|
||||
}, renderAndRun);
|
||||
}
|
||||
}).version(`${MAIN_MACRO.VERSION} (Claude Code)`, '-v, --version', 'Output the version number');
|
||||
}).version(`${MACRO.VERSION} (Claude Code)`, '-v, --version', 'Output the version number');
|
||||
|
||||
// Worktree flags
|
||||
program.option('-w, --worktree [name]', 'Create a new git worktree for this session (optionally specify a name)');
|
||||
|
||||
@@ -55,7 +55,6 @@ import {
|
||||
stripSignatureBlocks,
|
||||
} from './utils/messages.js'
|
||||
import { generateToolUseSummary } from './services/toolUseSummary/toolUseSummaryGenerator.js'
|
||||
import { prependUserContext, appendSystemContext } from './utils/api.js'
|
||||
import {
|
||||
createAttachmentMessage,
|
||||
filterDuplicateMemoryAttachments,
|
||||
@@ -446,9 +445,7 @@ async function* queryLoop(
|
||||
messagesForQuery = collapseResult.messages
|
||||
}
|
||||
|
||||
const fullSystemPrompt = asSystemPrompt(
|
||||
appendSystemContext(systemPrompt, systemContext),
|
||||
)
|
||||
const fullSystemPrompt = asSystemPrompt(systemPrompt)
|
||||
|
||||
queryCheckpoint('query_autocompact_start')
|
||||
const { compactionResult, consecutiveFailures } = await deps.autocompact(
|
||||
@@ -657,7 +654,7 @@ async function* queryLoop(
|
||||
let streamingFallbackOccured = false
|
||||
queryCheckpoint('query_api_streaming_start')
|
||||
for await (const message of deps.callModel({
|
||||
messages: prependUserContext(messagesForQuery, userContext),
|
||||
messages: messagesForQuery,
|
||||
systemPrompt: fullSystemPrompt,
|
||||
thinkingConfig: toolUseContext.options.thinkingConfig,
|
||||
tools: toolUseContext.options.tools,
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import type { SDKMessage } from '../entrypoints/agentSdkTypes.js'
|
||||
import type { SDKMessage } from '../entrypoints/agentSdkTypes.ts'
|
||||
import type {
|
||||
SDKControlCancelRequest,
|
||||
SDKControlPermissionRequest,
|
||||
SDKControlRequest,
|
||||
SDKControlResponse,
|
||||
} from '../entrypoints/sdk/controlTypes.js'
|
||||
} from '../entrypoints/sdk/controlTypes.ts'
|
||||
import { logForDebugging } from '../utils/debug.js'
|
||||
import { logError } from '../utils/log.js'
|
||||
import {
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { randomUUID } from 'crypto'
|
||||
import { getOauthConfig } from '../constants/oauth.js'
|
||||
import type { SDKMessage } from '../entrypoints/agentSdkTypes.js'
|
||||
import type { SDKMessage } from '../entrypoints/agentSdkTypes.ts'
|
||||
import type {
|
||||
SDKControlCancelRequest,
|
||||
SDKControlRequest,
|
||||
SDKControlRequestInner,
|
||||
SDKControlResponse,
|
||||
} from '../entrypoints/sdk/controlTypes.js'
|
||||
} from '../entrypoints/sdk/controlTypes.ts'
|
||||
import { logForDebugging } from '../utils/debug.js'
|
||||
import { errorMessage } from '../utils/errors.js'
|
||||
import { logError } from '../utils/log.js'
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { randomUUID } from 'crypto'
|
||||
import type { SDKControlPermissionRequest } from '../entrypoints/sdk/controlTypes.js'
|
||||
import type { SDKControlPermissionRequest } from '../entrypoints/sdk/controlTypes.ts'
|
||||
import type { Tool } from '../Tool.js'
|
||||
import type { AssistantMessage } from '../types/message.js'
|
||||
import { jsonStringify } from '../utils/slowOperations.js'
|
||||
|
||||
@@ -7,7 +7,7 @@ import type {
|
||||
SDKStatusMessage,
|
||||
SDKSystemMessage,
|
||||
SDKToolProgressMessage,
|
||||
} from '../entrypoints/agentSdkTypes.js'
|
||||
} from '../entrypoints/agentSdkTypes.ts'
|
||||
import type {
|
||||
AssistantMessage,
|
||||
Message,
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
* Both files now import from this shared location instead of each other.
|
||||
*/
|
||||
|
||||
import { HOOK_EVENTS, type HookEvent } from 'src/entrypoints/agentSdkTypes.js'
|
||||
import { HOOK_EVENTS, type HookEvent } from 'src/entrypoints/agentSdkTypes.ts'
|
||||
import { z } from 'zod/v4'
|
||||
import { lazySchema } from '../utils/lazySchema.js'
|
||||
import { SHELL_TYPES } from '../utils/shell/shellProvider.js'
|
||||
|
||||
@@ -267,7 +267,7 @@ import { useTeammateLifecycleNotification } from 'src/hooks/notifs/useTeammateSh
|
||||
import { useFastModeNotification } from 'src/hooks/notifs/useFastModeNotification.js';
|
||||
import { AutoRunIssueNotification, shouldAutoRunIssue, getAutoRunIssueReasonText, getAutoRunCommand, type AutoRunIssueReason } from '../utils/autoRunIssue.js';
|
||||
import type { HookProgress } from '../types/hooks.js';
|
||||
import { TungstenLiveMonitor } from '../tools/TungstenTool/TungstenLiveMonitor.js';
|
||||
import { TungstenLiveMonitor } from '../tools/TungstenTool/TungstenLiveMonitor.ts';
|
||||
/* eslint-disable @typescript-eslint/no-require-imports */
|
||||
const WebBrowserPanelModule = feature('WEB_BROWSER_TOOL') ? require('../tools/WebBrowserTool/WebBrowserPanel.js') as typeof import('../tools/WebBrowserTool/WebBrowserPanel.js') : null;
|
||||
/* eslint-enable @typescript-eslint/no-require-imports */
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/* eslint-disable eslint-plugin-n/no-unsupported-features/node-builtins */
|
||||
|
||||
import type { SDKMessage } from '../entrypoints/agentSdkTypes.js'
|
||||
import type { SDKMessage } from '../entrypoints/agentSdkTypes.ts'
|
||||
import type {
|
||||
SDKControlPermissionRequest,
|
||||
StdoutMessage,
|
||||
} from '../entrypoints/sdk/controlTypes.js'
|
||||
} from '../entrypoints/sdk/controlTypes.ts'
|
||||
import type { RemotePermissionResponse } from '../remote/RemoteSessionManager.js'
|
||||
import { logForDebugging } from '../utils/debug.js'
|
||||
import { jsonParse, jsonStringify } from '../utils/slowOperations.js'
|
||||
|
||||
@@ -41,7 +41,7 @@ import {
|
||||
type ConnectorTextBlock,
|
||||
type ConnectorTextDelta,
|
||||
isConnectorTextBlock,
|
||||
} from '../../types/connectorText.js'
|
||||
} from '../../types/connectorText.ts'
|
||||
import type {
|
||||
AssistantMessage,
|
||||
Message,
|
||||
|
||||
@@ -8,7 +8,7 @@ import type {
|
||||
BetaStopReason,
|
||||
} from '@anthropic-ai/sdk/resources/beta/messages/messages.mjs'
|
||||
import { AFK_MODE_BETA_HEADER } from 'src/constants/betas.js'
|
||||
import type { SDKAssistantMessageError } from 'src/entrypoints/agentSdkTypes.js'
|
||||
import type { SDKAssistantMessageError } from 'src/entrypoints/agentSdkTypes.ts'
|
||||
import type {
|
||||
AssistantMessage,
|
||||
Message,
|
||||
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
setLastApiCompletionTimestamp,
|
||||
} from 'src/bootstrap/state.js'
|
||||
import type { QueryChainTracking } from 'src/Tool.js'
|
||||
import { isConnectorTextBlock } from 'src/types/connectorText.js'
|
||||
import { isConnectorTextBlock } from 'src/types/connectorText.ts'
|
||||
import type { AssistantMessage } from 'src/types/message.js'
|
||||
import { logForDebugging } from 'src/utils/debug.js'
|
||||
import type { EffortLevel } from 'src/utils/effort.js'
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { ToolUseBlock } from '@anthropic-ai/sdk/resources';
|
||||
import { getRemoteSessionUrl } from '../../constants/product.js';
|
||||
import { OUTPUT_FILE_TAG, REMOTE_REVIEW_PROGRESS_TAG, REMOTE_REVIEW_TAG, STATUS_TAG, SUMMARY_TAG, TASK_ID_TAG, TASK_NOTIFICATION_TAG, TASK_TYPE_TAG, TOOL_USE_ID_TAG, ULTRAPLAN_TAG } from '../../constants/xml.js';
|
||||
import type { SDKAssistantMessage, SDKMessage } from '../../entrypoints/agentSdkTypes.js';
|
||||
import type { SDKAssistantMessage, SDKMessage } from '../../entrypoints/agentSdkTypes.ts';
|
||||
import type { SetAppState, Task, TaskContext, TaskStateBase } from '../../Task.js';
|
||||
import { createTaskStateBase, generateTaskId } from '../../Task.js';
|
||||
import { TodoWriteTool } from '../../tools/TodoWriteTool/TodoWriteTool.js';
|
||||
|
||||
@@ -57,7 +57,7 @@ import { TodoWriteTool } from './tools/TodoWriteTool/TodoWriteTool.js'
|
||||
import { ExitPlanModeV2Tool } from './tools/ExitPlanModeTool/ExitPlanModeV2Tool.js'
|
||||
import { TestingPermissionTool } from './tools/testing/TestingPermissionTool.js'
|
||||
import { GrepTool } from './tools/GrepTool/GrepTool.js'
|
||||
import { TungstenTool } from './tools/TungstenTool/TungstenTool.js'
|
||||
import { TungstenTool } from './tools/TungstenTool/TungstenTool.ts'
|
||||
// Lazy require to break circular dependency: tools.ts -> TeamCreateTool/TeamDeleteTool -> ... -> tools.ts
|
||||
/* eslint-disable @typescript-eslint/no-require-imports */
|
||||
const getTeamCreateTool = () =>
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
import { buildTool } from '../../Tool.js'
|
||||
import { z } from 'zod/v4'
|
||||
|
||||
const inputSchema = z.strictObject({})
|
||||
|
||||
const outputSchema = z.strictObject({})
|
||||
|
||||
export const TungstenTool = buildTool({
|
||||
name: 'tungsten',
|
||||
searchHint: 'manage tmux-based tasks',
|
||||
maxResultSizeChars: 1024,
|
||||
strict: true,
|
||||
async description() {
|
||||
return 'Tungsten integration is unavailable in this restored source snapshot.'
|
||||
},
|
||||
async prompt() {
|
||||
return 'Tungsten is unavailable in this build environment.'
|
||||
},
|
||||
get inputSchema() {
|
||||
return inputSchema
|
||||
},
|
||||
get outputSchema() {
|
||||
return outputSchema
|
||||
},
|
||||
isEnabled() {
|
||||
return false
|
||||
},
|
||||
userFacingName() {
|
||||
return 'Tungsten'
|
||||
},
|
||||
async call() {
|
||||
return {
|
||||
data: {},
|
||||
isError: true,
|
||||
content: 'Tungsten tool is not available in this environment.',
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
export function clearSessionsWithTungstenUsage() {}
|
||||
|
||||
export function resetInitializationState() {}
|
||||
@@ -1 +0,0 @@
|
||||
export const WORKFLOW_TOOL_NAME = 'workflow';
|
||||
@@ -1,29 +0,0 @@
|
||||
/** @typedef {import('../services/api/claude.js').BetaContentBlock} BetaContentBlock */
|
||||
/** @typedef {import('../services/api/claude.js').BetaContentBlockParam} BetaContentBlockParam */
|
||||
|
||||
/**
|
||||
* @typedef {object} ConnectorTextBlock
|
||||
* @property {'connector_text'} type
|
||||
* @property {string} connector_text
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} ConnectorTextDelta
|
||||
* @property {'connector_text_delta'} type
|
||||
* @property {string} connector_text
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {unknown} block
|
||||
* @returns {block is ConnectorTextBlock}
|
||||
*/
|
||||
export function isConnectorTextBlock(block) {
|
||||
return (
|
||||
Boolean(block) &&
|
||||
typeof block === 'object' &&
|
||||
block.type === 'connector_text' &&
|
||||
typeof block.connector_text === 'string'
|
||||
)
|
||||
}
|
||||
|
||||
export { }
|
||||
@@ -6,12 +6,12 @@ import {
|
||||
HOOK_EVENTS,
|
||||
type HookInput,
|
||||
type PermissionUpdate,
|
||||
} from 'src/entrypoints/agentSdkTypes.js'
|
||||
} from 'src/entrypoints/agentSdkTypes.ts'
|
||||
import type {
|
||||
HookJSONOutput,
|
||||
AsyncHookJSONOutput,
|
||||
SyncHookJSONOutput,
|
||||
} from 'src/entrypoints/agentSdkTypes.js'
|
||||
} from 'src/entrypoints/agentSdkTypes.ts'
|
||||
import type { Message } from 'src/types/message.js'
|
||||
import type { PermissionResult } from 'src/utils/permissions/PermissionResult.js'
|
||||
import { permissionBehaviorSchema } from 'src/utils/permissions/PermissionRule.js'
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { ContentBlockParam } from '@anthropic-ai/sdk/resources/messages.mjs'
|
||||
import type { UUID } from 'crypto'
|
||||
import type React from 'react'
|
||||
import type { PermissionResult } from '../entrypoints/agentSdkTypes.js'
|
||||
import type { PermissionResult } from '../entrypoints/agentSdkTypes.ts'
|
||||
import type { Key } from '../ink.js'
|
||||
import type { PastedContent } from '../utils/config.js'
|
||||
import type { ImageDimensions } from '../utils/imageResizer.js'
|
||||
|
||||
110
src/utils/api.ts
110
src/utils/api.ts
@@ -438,39 +438,14 @@ export function appendSystemContext(
|
||||
systemPrompt: SystemPrompt,
|
||||
context: { [k: string]: string },
|
||||
): string[] {
|
||||
return [
|
||||
...systemPrompt,
|
||||
Object.entries(context)
|
||||
.map(([key, value]) => `${key}: ${value}`)
|
||||
.join('\n'),
|
||||
].filter(Boolean)
|
||||
return systemPrompt
|
||||
}
|
||||
|
||||
export function prependUserContext(
|
||||
messages: Message[],
|
||||
context: { [k: string]: string },
|
||||
): Message[] {
|
||||
if (process.env.NODE_ENV === 'test') {
|
||||
return messages
|
||||
}
|
||||
|
||||
if (Object.entries(context).length === 0) {
|
||||
return messages
|
||||
}
|
||||
|
||||
return [
|
||||
createUserMessage({
|
||||
content: `<system-reminder>\nAs you answer the user's questions, you can use the following context:\n${Object.entries(
|
||||
context,
|
||||
)
|
||||
.map(([key, value]) => `# ${key}\n${value}`)
|
||||
.join('\n')}
|
||||
|
||||
IMPORTANT: this context may or may not be relevant to your tasks. You should not respond to this context unless it is highly relevant to your task.\n</system-reminder>\n`,
|
||||
isMeta: true,
|
||||
}),
|
||||
...messages,
|
||||
]
|
||||
return messages
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -480,86 +455,7 @@ export async function logContextMetrics(
|
||||
mcpConfigs: Record<string, ScopedMcpServerConfig>,
|
||||
toolPermissionContext: ToolPermissionContext,
|
||||
): Promise<void> {
|
||||
// Early return if logging is disabled
|
||||
if (isAnalyticsDisabled()) {
|
||||
return
|
||||
}
|
||||
const [{ tools: mcpTools }, tools, userContext, systemContext] =
|
||||
await Promise.all([
|
||||
prefetchAllMcpResources(mcpConfigs),
|
||||
getTools(toolPermissionContext),
|
||||
getUserContext(),
|
||||
getSystemContext(),
|
||||
])
|
||||
// Extract individual context sizes and calculate total
|
||||
const gitStatusSize = systemContext.gitStatus?.length ?? 0
|
||||
const claudeMdSize = userContext.claudeMd?.length ?? 0
|
||||
|
||||
// Calculate total context size
|
||||
const totalContextSize = gitStatusSize + claudeMdSize
|
||||
|
||||
// Get file count using ripgrep (rounded to nearest power of 10 for privacy)
|
||||
const currentDir = getCwd()
|
||||
const ignorePatternsByRoot = getFileReadIgnorePatterns(toolPermissionContext)
|
||||
const normalizedIgnorePatterns = normalizePatternsToPath(
|
||||
ignorePatternsByRoot,
|
||||
currentDir,
|
||||
)
|
||||
const fileCount = await countFilesRoundedRg(
|
||||
currentDir,
|
||||
AbortSignal.timeout(1000),
|
||||
normalizedIgnorePatterns,
|
||||
)
|
||||
|
||||
// Calculate tool metrics
|
||||
let mcpToolsCount = 0
|
||||
let mcpServersCount = 0
|
||||
let mcpToolsTokens = 0
|
||||
let nonMcpToolsCount = 0
|
||||
let nonMcpToolsTokens = 0
|
||||
|
||||
const nonMcpTools = tools.filter(tool => !tool.isMcp)
|
||||
mcpToolsCount = mcpTools.length
|
||||
nonMcpToolsCount = nonMcpTools.length
|
||||
|
||||
// Extract unique server names from MCP tool names (format: mcp__servername__toolname)
|
||||
const serverNames = new Set<string>()
|
||||
for (const tool of mcpTools) {
|
||||
const parts = tool.name.split('__')
|
||||
if (parts.length >= 3 && parts[1]) {
|
||||
serverNames.add(parts[1])
|
||||
}
|
||||
}
|
||||
mcpServersCount = serverNames.size
|
||||
|
||||
// Estimate tool tokens locally for analytics (avoids N API calls per session)
|
||||
// Use inputJSONSchema (plain JSON Schema) when available, otherwise convert Zod schema
|
||||
for (const tool of mcpTools) {
|
||||
const schema =
|
||||
'inputJSONSchema' in tool && tool.inputJSONSchema
|
||||
? tool.inputJSONSchema
|
||||
: zodToJsonSchema(tool.inputSchema)
|
||||
mcpToolsTokens += roughTokenCountEstimation(jsonStringify(schema))
|
||||
}
|
||||
for (const tool of nonMcpTools) {
|
||||
const schema =
|
||||
'inputJSONSchema' in tool && tool.inputJSONSchema
|
||||
? tool.inputJSONSchema
|
||||
: zodToJsonSchema(tool.inputSchema)
|
||||
nonMcpToolsTokens += roughTokenCountEstimation(jsonStringify(schema))
|
||||
}
|
||||
|
||||
logEvent('tengu_context_size', {
|
||||
git_status_size: gitStatusSize,
|
||||
claude_md_size: claudeMdSize,
|
||||
total_context_size: totalContextSize,
|
||||
project_file_count_rounded: fileCount,
|
||||
mcp_tools_count: mcpToolsCount,
|
||||
mcp_servers_count: mcpServersCount,
|
||||
mcp_tools_tokens: mcpToolsTokens,
|
||||
non_mcp_tools_count: nonMcpToolsCount,
|
||||
non_mcp_tools_tokens: nonMcpToolsTokens,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: Generalize this to all tools
|
||||
|
||||
@@ -178,7 +178,7 @@ import type { MCPServerConnection } from '../services/mcp/types.js'
|
||||
import type {
|
||||
HookEvent,
|
||||
SyncHookJSONOutput,
|
||||
} from 'src/entrypoints/agentSdkTypes.js'
|
||||
} from 'src/entrypoints/agentSdkTypes.ts'
|
||||
import {
|
||||
checkForAsyncHookResponses,
|
||||
removeDeliveredAsyncHooks,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { SDKMessage } from 'src/entrypoints/agentSdkTypes.js'
|
||||
import type { SDKMessage } from 'src/entrypoints/agentSdkTypes.ts'
|
||||
import { checkGate_CACHED_OR_BLOCKING } from '../../../services/analytics/growthbook.js'
|
||||
import { isPolicyAllowed } from '../../../services/policyLimits/index.js'
|
||||
import { detectCurrentRepositoryWithHost } from '../../detectRepository.js'
|
||||
|
||||
@@ -6,7 +6,7 @@ import { getFeatureValue_CACHED_MAY_BE_STALE } from 'src/services/analytics/grow
|
||||
import { getAPIProvider } from './model/providers.js'
|
||||
import { get3PModelCapabilityOverride } from './model/modelSupportOverrides.js'
|
||||
import { isEnvTruthy } from './envUtils.js'
|
||||
import type { EffortLevel } from 'src/entrypoints/sdk/runtimeTypes.js'
|
||||
import type { EffortLevel } from 'src/entrypoints/sdk/runtimeTypes.ts'
|
||||
|
||||
export type { EffortLevel }
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import chalk from 'chalk'
|
||||
import { writeSync } from 'fs'
|
||||
import memoize from 'lodash-es/memoize.js'
|
||||
import { onExit } from 'signal-exit'
|
||||
import type { ExitReason } from 'src/entrypoints/agentSdkTypes.js'
|
||||
import type { ExitReason } from 'src/entrypoints/agentSdkTypes.ts'
|
||||
import {
|
||||
getIsInteractive,
|
||||
getIsScrollDraining,
|
||||
|
||||
@@ -106,7 +106,7 @@ import type {
|
||||
ExitReason,
|
||||
SyncHookJSONOutput,
|
||||
AsyncHookJSONOutput,
|
||||
} from 'src/entrypoints/agentSdkTypes.js'
|
||||
} from 'src/entrypoints/agentSdkTypes.ts'
|
||||
import type { StatusLineCommandInput } from '../types/statusLine.js'
|
||||
import type { ElicitResult } from '@modelcontextprotocol/sdk/types.js'
|
||||
import type { FileSuggestionCommandInput } from '../types/fileSuggestion.js'
|
||||
|
||||
@@ -2,7 +2,7 @@ import type {
|
||||
AsyncHookJSONOutput,
|
||||
HookEvent,
|
||||
SyncHookJSONOutput,
|
||||
} from 'src/entrypoints/agentSdkTypes.js'
|
||||
} from 'src/entrypoints/agentSdkTypes.ts'
|
||||
import { logForDebugging } from '../debug.js'
|
||||
import type { ShellCommand } from '../ShellCommand.js'
|
||||
import { invalidateSessionEnvCache } from '../sessionEnvironment.js'
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { randomUUID } from 'crypto'
|
||||
import type { HookEvent } from 'src/entrypoints/agentSdkTypes.js'
|
||||
import type { HookEvent } from 'src/entrypoints/agentSdkTypes.ts'
|
||||
import { query } from '../../query.js'
|
||||
import { logEvent } from '../../services/analytics/index.js'
|
||||
import type { AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS } from '../../services/analytics/metadata.js'
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import axios from 'axios'
|
||||
import type { HookEvent } from 'src/entrypoints/agentSdkTypes.js'
|
||||
import type { HookEvent } from 'src/entrypoints/agentSdkTypes.ts'
|
||||
import { createCombinedAbortSignal } from '../combinedAbortSignal.js'
|
||||
import { logForDebugging } from '../debug.js'
|
||||
import { errorMessage } from '../errors.js'
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { randomUUID } from 'crypto'
|
||||
import type { HookEvent } from 'src/entrypoints/agentSdkTypes.js'
|
||||
import type { HookEvent } from 'src/entrypoints/agentSdkTypes.ts'
|
||||
import { queryModelWithoutStreaming } from '../../services/api/claude.js'
|
||||
import type { ToolUseContext } from '../../Tool.js'
|
||||
import type { Message } from '../../types/message.js'
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import memoize from 'lodash-es/memoize.js'
|
||||
import type { HookEvent } from 'src/entrypoints/agentSdkTypes.js'
|
||||
import type { HookEvent } from 'src/entrypoints/agentSdkTypes.ts'
|
||||
import { getRegisteredHooks } from '../../bootstrap/state.js'
|
||||
import type { AppState } from '../../state/AppState.js'
|
||||
import {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { resolve } from 'path'
|
||||
import type { HookEvent } from 'src/entrypoints/agentSdkTypes.js'
|
||||
import type { HookEvent } from 'src/entrypoints/agentSdkTypes.ts'
|
||||
import { getSessionId } from '../../bootstrap/state.js'
|
||||
import type { AppState } from '../../state/AppState.js'
|
||||
import type { EditableSettingSource } from '../settings/constants.js'
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { HOOK_EVENTS, type HookEvent } from 'src/entrypoints/agentSdkTypes.js'
|
||||
import { HOOK_EVENTS, type HookEvent } from 'src/entrypoints/agentSdkTypes.ts'
|
||||
import type { AppState } from 'src/state/AppState.js'
|
||||
import { logForDebugging } from '../debug.js'
|
||||
import type { HooksSettings } from '../settings/types.js'
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { HOOK_EVENTS } from 'src/entrypoints/agentSdkTypes.js'
|
||||
import { HOOK_EVENTS } from 'src/entrypoints/agentSdkTypes.ts'
|
||||
import type { AppState } from 'src/state/AppState.js'
|
||||
import { logForDebugging } from '../debug.js'
|
||||
import type { HooksSettings } from '../settings/types.js'
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { HOOK_EVENTS, type HookEvent } from 'src/entrypoints/agentSdkTypes.js'
|
||||
import { HOOK_EVENTS, type HookEvent } from 'src/entrypoints/agentSdkTypes.ts'
|
||||
import type { AppState } from 'src/state/AppState.js'
|
||||
import type { Message } from 'src/types/message.js'
|
||||
import { logForDebugging } from '../debug.js'
|
||||
|
||||
@@ -37,7 +37,7 @@ import {
|
||||
getRequestTooLargeErrorMessage,
|
||||
} from '../services/api/errors.js'
|
||||
import type { AnyObject, Progress } from '../Tool.js'
|
||||
import { isConnectorTextBlock } from '../types/connectorText.js'
|
||||
import { isConnectorTextBlock } from '../types/connectorText.ts'
|
||||
import type {
|
||||
AssistantMessage,
|
||||
AttachmentMessage,
|
||||
@@ -102,7 +102,7 @@ import type {
|
||||
import type {
|
||||
HookEvent,
|
||||
SDKAssistantMessageError,
|
||||
} from 'src/entrypoints/agentSdkTypes.js'
|
||||
} from 'src/entrypoints/agentSdkTypes.ts'
|
||||
import { EXPLORE_AGENT } from 'src/tools/AgentTool/built-in/exploreAgent.js'
|
||||
import { PLAN_AGENT } from 'src/tools/AgentTool/built-in/planAgent.js'
|
||||
import { areExplorePlanAgentsEnabled } from 'src/tools/AgentTool/builtInAgents.js'
|
||||
|
||||
@@ -10,7 +10,7 @@ import type {
|
||||
SDKCompactBoundaryMessage,
|
||||
SDKMessage,
|
||||
SDKRateLimitInfo,
|
||||
} from 'src/entrypoints/agentSdkTypes.js'
|
||||
} from 'src/entrypoints/agentSdkTypes.ts'
|
||||
import type { ClaudeAILimits } from 'src/services/claudeAiLimits.js'
|
||||
import { EXIT_PLAN_MODE_V2_TOOL_NAME } from 'src/tools/ExitPlanModeTool/constants.js'
|
||||
import type {
|
||||
|
||||
@@ -6,7 +6,7 @@ import type {
|
||||
ApiKeySource,
|
||||
PermissionMode,
|
||||
SDKMessage,
|
||||
} from 'src/entrypoints/agentSdkTypes.js'
|
||||
} from 'src/entrypoints/agentSdkTypes.ts'
|
||||
import {
|
||||
AGENT_TOOL_NAME,
|
||||
LEGACY_AGENT_TOOL_NAME,
|
||||
|
||||
@@ -42,7 +42,7 @@ const VERIFY_PLAN_EXECUTION_TOOL_NAME =
|
||||
: null
|
||||
const WORKFLOW_TOOL_NAME = feature('WORKFLOW_SCRIPTS')
|
||||
? (
|
||||
require('../../tools/WorkflowTool/constants.js') as typeof import('../../tools/WorkflowTool/constants.js')
|
||||
require('../../tools/WorkflowTool/constants.ts') as typeof import('../../tools/WorkflowTool/constants.ts')
|
||||
).WORKFLOW_TOOL_NAME
|
||||
: null
|
||||
/* eslint-enable @typescript-eslint/no-require-imports */
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import memoize from 'lodash-es/memoize.js'
|
||||
import type { HookEvent } from 'src/entrypoints/agentSdkTypes.js'
|
||||
import type { HookEvent } from 'src/entrypoints/agentSdkTypes.ts'
|
||||
import {
|
||||
clearRegisteredPluginHooks,
|
||||
getRegisteredHooks,
|
||||
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
getSessionId,
|
||||
isSessionPersistenceDisabled,
|
||||
} from 'src/bootstrap/state.js'
|
||||
import type { SDKMessage } from 'src/entrypoints/agentSdkTypes.js'
|
||||
import type { SDKMessage } from 'src/entrypoints/agentSdkTypes.ts'
|
||||
import type { CanUseToolFn } from '../hooks/useCanUseTool.js'
|
||||
import { runTools } from '../services/tools/toolOrchestration.js'
|
||||
import { findToolByName, type Tool, type Tools } from '../Tool.js'
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
*/
|
||||
import { feature } from 'bun:bundle'
|
||||
import { registerHookCallbacks } from '../bootstrap/state.js'
|
||||
import type { HookInput, HookJSONOutput } from '../entrypoints/agentSdkTypes.js'
|
||||
import type { HookInput, HookJSONOutput } from '../entrypoints/agentSdkTypes.ts'
|
||||
import {
|
||||
type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
|
||||
logEvent,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { feature } from 'bun:bundle'
|
||||
import { open } from 'fs/promises'
|
||||
import { basename, dirname, join, sep } from 'path'
|
||||
import type { ModelUsage } from 'src/entrypoints/agentSdkTypes.js'
|
||||
import type { ModelUsage } from 'src/entrypoints/agentSdkTypes.ts'
|
||||
import type { Entry, TranscriptMessage } from '../types/logs.js'
|
||||
import { logForDebugging } from './debug.js'
|
||||
import { errorMessage, isENOENT } from './errors.js'
|
||||
|
||||
@@ -2,7 +2,7 @@ import { feature } from 'bun:bundle'
|
||||
import { randomBytes } from 'crypto'
|
||||
import { open } from 'fs/promises'
|
||||
import { join } from 'path'
|
||||
import type { ModelUsage } from '../entrypoints/agentSdkTypes.js'
|
||||
import type { ModelUsage } from '../entrypoints/agentSdkTypes.ts'
|
||||
import { logForDebugging } from './debug.js'
|
||||
import { getClaudeConfigHomeDir } from './envUtils.js'
|
||||
import { errorMessage } from './errors.js'
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
* - Strips tool list and model info from init messages
|
||||
*/
|
||||
|
||||
import type { SDKAssistantMessage } from 'src/entrypoints/agentSdkTypes.js'
|
||||
import type { StdoutMessage } from 'src/entrypoints/sdk/controlTypes.js'
|
||||
import type { SDKAssistantMessage } from 'src/entrypoints/agentSdkTypes.ts'
|
||||
import type { StdoutMessage } from 'src/entrypoints/sdk/controlTypes.ts'
|
||||
import { FILE_EDIT_TOOL_NAME } from 'src/tools/FileEditTool/constants.js'
|
||||
import { FILE_READ_TOOL_NAME } from 'src/tools/FileReadTool/prompt.js'
|
||||
import { FILE_WRITE_TOOL_NAME } from 'src/tools/FileWriteTool/prompt.js'
|
||||
|
||||
@@ -9,7 +9,7 @@ import { isPolicyAllowed } from 'src/services/policyLimits/index.js';
|
||||
import { z } from 'zod/v4';
|
||||
import { getTeleportErrors, TeleportError, type TeleportLocalErrorType } from '../components/TeleportError.js';
|
||||
import { getOauthConfig } from '../constants/oauth.js';
|
||||
import type { SDKMessage } from '../entrypoints/agentSdkTypes.js';
|
||||
import type { SDKMessage } from '../entrypoints/agentSdkTypes.ts';
|
||||
import type { Root } from '../ink.js';
|
||||
import { KeybindingSetup } from '../keybindings/KeybindingProviderSetup.js';
|
||||
import { queryHaiku } from '../services/api/claude.js';
|
||||
|
||||
@@ -8,7 +8,7 @@ import type {
|
||||
ToolResultBlockParam,
|
||||
ToolUseBlock,
|
||||
} from '@anthropic-ai/sdk/resources'
|
||||
import type { SDKMessage } from '../../entrypoints/agentSdkTypes.js'
|
||||
import type { SDKMessage } from '../../entrypoints/agentSdkTypes.ts'
|
||||
import { EXIT_PLAN_MODE_V2_TOOL_NAME } from '../../tools/ExitPlanModeTool/constants.js'
|
||||
import { logForDebugging } from '../debug.js'
|
||||
import { sleep } from '../sleep.js'
|
||||
|
||||
Reference in New Issue
Block a user