surshar / claude-code
Claude Code is an agentic coding tool that lives in your terminal, understands your codebase, and helps you code faster by executing routine tasks, explaining complex code, and handling git workflows - all through natural language commands.
Built by Anthropic · github.com/anthropics
| @@ -467,7 +467,7 @@ | ||
| 467 | 467 | const { Box, Text, Button, Select } = await $.ui.resolve(e) |
| 468 | 468 | wasDrawnSinceProbe = true |
| 469 | 469 | columns = e.viewport?.columns ?? columns |
| 470 | - model = { ...model, isFocused: e.props.focused } | |
| 470 | + model = { ...model, isFocused: e.props.isFocused } | |
| 471 | 471 | |
| 472 | 472 | return Views.paneView( |
| 473 | 473 | { |
| @@ -20,7 +20,7 @@ | ||
| 20 | 20 | ): TodoProgress { |
| 21 | 21 | const latest = messages |
| 22 | 22 | .flatMap(message => message.toolUses) |
| 23 | - .findLast(use => use.name === Tools.TODO_TOOL) | |
| 23 | + .findLast(use => use.tool === Tools.TODO_TOOL) | |
| 24 | 24 | const statuses = arrayOf(latest?.input.todos).map(todo => |
| 25 | 25 | isRecord(todo) ? todo.status : null, |
| 26 | 26 | ) |
| @@ -7,16 +7,18 @@ | ||
| 7 | 7 | `$.telemetry.mark({ feature, kind, reason? })` marks one use of a feature as |
| 8 | 8 | the CLI's own feature events do, `tengu_feature_<kind>` with a |
| 9 | 9 | `feature_name`. Each call is one POST to the event-logging ingest with the |
| 10 | -session's own credential (`$.session.authorize()`, resolved once and held), | |
| 10 | +session's own credential (`$.session.authorize()`, resolved at each call), | |
| 11 | 11 | one attempt, nothing batched; a session with no first-party credential, or |
| 12 | 12 | an ingest that refuses, rejects the caller's promise. |
| 13 | 13 | |
| 14 | 14 | It sends nothing wherever the CLI's own analytics are off: under |
| 15 | 15 | `DISABLE_TELEMETRY`, `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC` or |
| 16 | -`DO_NOT_TRACK`, on a third-party provider (Bedrock, Vertex, Foundry and | |
| 17 | -kin, unless the host manages the provider), on a deployment with its own | |
| 18 | -OAuth URL, and under test; each read through `$.env` once a session. The | |
| 19 | -row's `user_type` is `ant` when `USER_TYPE` says so, else `external`. | |
| 16 | +`DO_NOT_TRACK`, on any third-party provider (Bedrock, Vertex, Foundry and | |
| 17 | +kin), and on a deployment with its own OAuth URL. Each is read through | |
| 18 | +`$.env` at every call, rows go one after another, and the credential is | |
| 19 | +authorized afresh right before each POST, so a session that has since moved | |
| 20 | +to a third-party provider or a cloud gateway sends nothing more. The row's | |
| 21 | +`user_type` is `ant` when `USER_TYPE` says so, else `external`. | |
| 20 | 22 | |
| 21 | 23 | Nothing free-form reaches a row. An event name and every property key is a |
| 22 | 24 | snake_case token; a value is a finite number, a boolean, or a Choice (a |
| @@ -1,5 +1,5 @@ | ||
| 1 | 1 | /** |
| 2 | - * What the plugin reads of the environment, once a session: the | |
| 2 | + * What the plugin reads of the environment before each row: the | |
| 3 | 3 | * build's user type and every switch that turns the CLI's analytics off. |
| 4 | 4 | * |
| 5 | 5 | * Each field is the variable's value as `$.env.get` answers it, undefined |
| @@ -7,12 +7,10 @@ | ||
| 7 | 7 | */ |
| 8 | 8 | export type Environment = { |
| 9 | 9 | readonly userType: string | undefined |
| 10 | - readonly nodeEnv: string | undefined | |
| 11 | 10 | readonly disableTelemetry: string | undefined |
| 12 | 11 | readonly disableNonessentialTraffic: string | undefined |
| 13 | 12 | readonly doNotTrack: string | undefined |
| 14 | 13 | readonly customOauthUrl: string | undefined |
| 15 | - readonly providerManagedByHost: string | undefined | |
| 16 | 14 | readonly useBedrock: string | undefined |
| 17 | 15 | readonly useVertex: string | undefined |
| 18 | 16 | readonly useFoundry: string | undefined |
| @@ -6,30 +6,27 @@ | ||
| 6 | 6 | * Whether the CLI's own analytics would be off here, read off the |
| 7 | 7 | * environment alone: then the plugin sends nothing either. |
| 8 | 8 | * |
| 9 | - * Off under test, when DISABLE_TELEMETRY or the nonessential-traffic switch | |
| 10 | - * is set at all or DO_NOT_TRACK is on, on a third-party provider the host | |
| 11 | - * does not manage, and on a deployment with its own OAuth URL. | |
| 9 | + * Off when DISABLE_TELEMETRY or the nonessential-traffic switch is set at | |
| 10 | + * all or DO_NOT_TRACK is on, on any third-party provider (no first-party | |
| 11 | + * credential rides there), and on a deployment with its own OAuth URL. | |
| 12 | 12 | * |
| 13 | 13 | * @param environment the variables as read once for the session |
| 14 | 14 | * @returns true when no row may be sent |
| 15 | 15 | */ |
| 16 | 16 | export function isAnalyticsOff(environment: Environment) { |
| 17 | - const isTest = environment.nodeEnv === 'test' | |
| 18 | 17 | const isPrivate = |
| 19 | 18 | isEnvSet(environment.disableTelemetry) || |
| 20 | 19 | isEnvSet(environment.disableNonessentialTraffic) || |
| 21 | 20 | isEnvTruthy(environment.doNotTrack) |
| 22 | - const isThirdParty = | |
| 23 | - !isEnvTruthy(environment.providerManagedByHost) && | |
| 24 | - [ | |
| 25 | - environment.useBedrock, | |
| 26 | - environment.useVertex, | |
| 27 | - environment.useFoundry, | |
| 28 | - environment.useAnthropicAws, | |
| 29 | - environment.useAnthropicGoogleCloud, | |
| 30 | - environment.useMantle, | |
| 31 | - ].some(isEnvTruthy) | |
| 21 | + const isThirdParty = [ | |
| 22 | + environment.useBedrock, | |
| 23 | + environment.useVertex, | |
| 24 | + environment.useFoundry, | |
| 25 | + environment.useAnthropicAws, | |
| 26 | + environment.useAnthropicGoogleCloud, | |
| 27 | + environment.useMantle, | |
| 28 | + ].some(isEnvTruthy) | |
| 32 | 29 | const isCustomDeployment = isEnvSet(environment.customOauthUrl?.trim()) |
| 33 | 30 | |
| 34 | - return isTest || isPrivate || isThirdParty || isCustomDeployment | |
| 31 | + return isPrivate || isThirdParty || isCustomDeployment | |
| 35 | 32 | } |
| @@ -23,16 +23,12 @@ | ||
| 23 | 23 | model: () => beneath.session.model(), |
| 24 | 24 | environment: async () => ({ |
| 25 | 25 | userType: await beneath.env.get('USER_TYPE'), |
| 26 | - nodeEnv: await beneath.env.get('NODE_ENV'), | |
| 27 | 26 | disableTelemetry: await beneath.env.get('DISABLE_TELEMETRY'), |
| 28 | 27 | disableNonessentialTraffic: await beneath.env.get( |
| 29 | 28 | 'CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC', |
| 30 | 29 | ), |
| 31 | 30 | doNotTrack: await beneath.env.get('DO_NOT_TRACK'), |
| 32 | 31 | customOauthUrl: await beneath.env.get('CLAUDE_CODE_CUSTOM_OAUTH_URL'), |
| 33 | - providerManagedByHost: await beneath.env.get( | |
| 34 | - 'CLAUDE_CODE_PROVIDER_MANAGED_BY_HOST', | |
| 35 | - ), | |
| 36 | 32 | useBedrock: await beneath.env.get('CLAUDE_CODE_USE_BEDROCK'), |
| 37 | 33 | useVertex: await beneath.env.get('CLAUDE_CODE_USE_VERTEX'), |
| 38 | 34 | useFoundry: await beneath.env.get('CLAUDE_CODE_USE_FOUNDRY'), |
| @@ -23,8 +23,8 @@ | ||
| 23 | 23 | model: () => Promise<string> |
| 24 | 24 | |
| 25 | 25 | /** |
| 26 | - * Reads what the row and the off switch need of the environment: | |
| 27 | - * the build's user type, and every variable that turns analytics off. | |
| 26 | + * Reads, at each call, what the row and the off switch need of the | |
| 27 | + * environment: the build's user type and every analytics-off variable. | |
| 28 | 28 | */ |
| 29 | 29 | environment: () => Promise<Environment> |
| 30 | 30 |
| @@ -4,60 +4,43 @@ | ||
| 4 | 4 | import type TelemetryTypes from '../telemetry-types' |
| 5 | 5 | |
| 6 | 6 | /** |
| 7 | - * Builds `$.telemetry`: `log` and `mark` check the entry, authorize once, build | |
| 8 | - * the first-party row and POST it to the ingest. | |
| 7 | + * Builds `$.telemetry`: `log` and `mark` check the entry, read the environment | |
| 8 | + * and authorize afresh, build the first-party row and POST it to the ingest. | |
| 9 | 9 | * |
| 10 | - * One POST per call, none batched, one attempt; a session with no credential to | |
| 11 | - * authorize, or an ingest that refuses, rejects the caller's promise. A failed | |
| 12 | - * authorize or environment read is not memoized, so a later call retries it. | |
| 10 | + * One POST per call, rows one after another, nothing kept between them: a | |
| 11 | + * session that has moved to a third-party provider or a gateway, or turned | |
| 12 | + * analytics off, sends nothing more; no credential or a refusal rejects. | |
| 13 | 13 | * |
| 14 | 14 | * @param deps the calls on the nouns beneath |
| 15 | - * @returns the `$.telemetry` interface, `log` and `mark`, each authorizing once | |
| 16 | - * (memoized) before it posts | |
| 15 | + * @returns the `$.telemetry` interface, `log` and `mark` | |
| 17 | 16 | */ |
| 18 | 17 | export function telemetryOf(deps: TelemetryDeps): TelemetryTypes.Telemetry { |
| 19 | - let held: ReturnType<TelemetryDeps['authorize']> | undefined | |
| 20 | - let read: ReturnType<TelemetryDeps['environment']> | undefined | |
| 18 | + let queue: Promise<unknown> = Promise.resolve() | |
| 21 | 19 | |
| 22 | 20 | async function post( |
| 23 | 21 | fields: Entries.Fields, |
| 24 | 22 | method: TelemetryTypes.Method, |
| 25 | 23 | ): Promise<void> { |
| 26 | - read ??= deps.environment().catch((error: unknown) => { | |
| 27 | - read = undefined | |
| 24 | + const environment = await deps.environment() | |
| 28 | 25 | |
| 29 | - throw error | |
| 30 | - }) | |
| 31 | - const environment = await read | |
| 32 | - | |
| 33 | 26 | if (isAnalyticsOff(environment)) { |
| 34 | 27 | return |
| 35 | 28 | } |
| 36 | 29 | |
| 37 | - if (!held) { | |
| 38 | - held = deps.authorize().catch((error: unknown) => { | |
| 39 | - held = undefined | |
| 30 | + const body = Entries.batchOf(fields, { | |
| 31 | + sessionId: await deps.id(), | |
| 32 | + model: await deps.model(), | |
| 33 | + userType: environment.userType === 'ant' ? 'ant' : 'external', | |
| 34 | + }) | |
| 35 | + const auth = await deps.authorize() | |
| 40 | 36 | |
| 41 | - throw error | |
| 42 | - }) | |
| 43 | - } | |
| 44 | - | |
| 45 | - const auth = await held | |
| 46 | - | |
| 47 | 37 | if (!auth) { |
| 48 | - held = undefined | |
| 49 | - | |
| 50 | 38 | throw Entries.refusal( |
| 51 | 39 | 'this session has no first-party credential to authorize', |
| 52 | 40 | method, |
| 53 | 41 | ) |
| 54 | 42 | } |
| 55 | 43 | |
| 56 | - const body = Entries.batchOf(fields, { | |
| 57 | - sessionId: await deps.id(), | |
| 58 | - model: await deps.model(), | |
| 59 | - userType: environment.userType === 'ant' ? 'ant' : 'external', | |
| 60 | - }) | |
| 61 | 44 | const response = await deps.fetch(Entries.INGEST_URL, { |
| 62 | 45 | method: 'POST', |
| 63 | 46 | headers: { |
| @@ -73,8 +56,18 @@ | ||
| 73 | 56 | } |
| 74 | 57 | } |
| 75 | 58 | |
| 59 | + function queued( | |
| 60 | + fields: Entries.Fields, | |
| 61 | + method: TelemetryTypes.Method, | |
| 62 | + ): Promise<void> { | |
| 63 | + const turn = queue.then(() => post(fields, method)) | |
| 64 | + queue = turn.catch(() => undefined) | |
| 65 | + | |
| 66 | + return turn | |
| 67 | + } | |
| 68 | + | |
| 76 | 69 | return { |
| 77 | - log: async entry => post(Entries.checkedFields(entry), 'log'), | |
| 78 | - mark: async entry => post(Entries.checkedMark(entry), 'mark'), | |
| 70 | + log: async entry => queued(Entries.checkedFields(entry), 'log'), | |
| 71 | + mark: async entry => queued(Entries.checkedMark(entry), 'mark'), | |
| 79 | 72 | } |
| 80 | 73 | } |