Skip to content

Commit 1a7c76e

Browse files
committed
mods: API renames in diff, telemetry rows queue and third-party providers send nothing
1 parent e62465d commit 1a7c76e

8 files changed

Lines changed: 50 additions & 64 deletions

File tree

‎mods/diff/hooks/register.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -467,7 +467,7 @@ export function register(on: On) {
467467
const { Box, Text, Button, Select } = await $.ui.resolve(e)
468468
wasDrawnSinceProbe = true
469469
columns = e.viewport?.columns ?? columns
470-
model = { ...model, isFocused: e.props.focused }
470+
model = { ...model, isFocused: e.props.isFocused }
471471

472472
return Views.paneView(
473473
{

‎mods/diff/hooks/todos/todo-progress-of.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ export function todoProgressOf(
2020
): TodoProgress {
2121
const latest = messages
2222
.flatMap(message => message.toolUses)
23-
.findLast(use => use.name === Tools.TODO_TOOL)
23+
.findLast(use => use.tool === Tools.TODO_TOOL)
2424
const statuses = arrayOf(latest?.input.todos).map(todo =>
2525
isRecord(todo) ? todo.status : null,
2626
)

‎mods/telemetry/README.md‎

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,18 @@ sends one event as one first-party row, `tengu_plugin_<event>`;
77
`$.telemetry.mark({ feature, kind, reason? })` marks one use of a feature as
88
the CLI's own feature events do, `tengu_feature_<kind>` with a
99
`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),
1111
one attempt, nothing batched; a session with no first-party credential, or
1212
an ingest that refuses, rejects the caller's promise.
1313

1414
It sends nothing wherever the CLI's own analytics are off: under
1515
`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`.
2022

2123
Nothing free-form reaches a row. An event name and every property key is a
2224
snake_case token; a value is a finite number, a boolean, or a Choice (a

‎mods/telemetry/hooks/environment/environment.ts‎

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,16 @@
11
/**
2-
* What the plugin reads of the environment, once a session: the
2+
* What the plugin reads of the environment before each row: the
33
* build's user type and every switch that turns the CLI's analytics off.
44
*
55
* Each field is the variable's value as `$.env.get` answers it, undefined
66
* when unset; analytics-off/ decides what they mean together.
77
*/
88
export type Environment = {
99
readonly userType: string | undefined
10-
readonly nodeEnv: string | undefined
1110
readonly disableTelemetry: string | undefined
1211
readonly disableNonessentialTraffic: string | undefined
1312
readonly doNotTrack: string | undefined
1413
readonly customOauthUrl: string | undefined
15-
readonly providerManagedByHost: string | undefined
1614
readonly useBedrock: string | undefined
1715
readonly useVertex: string | undefined
1816
readonly useFoundry: string | undefined

‎mods/telemetry/hooks/is-analytics-off/is-analytics-off.ts‎

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,30 +6,27 @@ import { isEnvTruthy } from './is-env-truthy'
66
* Whether the CLI's own analytics would be off here, read off the
77
* environment alone: then the plugin sends nothing either.
88
*
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.
1212
*
1313
* @param environment the variables as read once for the session
1414
* @returns true when no row may be sent
1515
*/
1616
export function isAnalyticsOff(environment: Environment) {
17-
const isTest = environment.nodeEnv === 'test'
1817
const isPrivate =
1918
isEnvSet(environment.disableTelemetry) ||
2019
isEnvSet(environment.disableNonessentialTraffic) ||
2120
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)
3229
const isCustomDeployment = isEnvSet(environment.customOauthUrl?.trim())
3330

34-
return isTest || isPrivate || isThirdParty || isCustomDeployment
31+
return isPrivate || isThirdParty || isCustomDeployment
3532
}

‎mods/telemetry/hooks/register.ts‎

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,12 @@ export function register(on: On) {
2323
model: () => beneath.session.model(),
2424
environment: async () => ({
2525
userType: await beneath.env.get('USER_TYPE'),
26-
nodeEnv: await beneath.env.get('NODE_ENV'),
2726
disableTelemetry: await beneath.env.get('DISABLE_TELEMETRY'),
2827
disableNonessentialTraffic: await beneath.env.get(
2928
'CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC',
3029
),
3130
doNotTrack: await beneath.env.get('DO_NOT_TRACK'),
3231
customOauthUrl: await beneath.env.get('CLAUDE_CODE_CUSTOM_OAUTH_URL'),
33-
providerManagedByHost: await beneath.env.get(
34-
'CLAUDE_CODE_PROVIDER_MANAGED_BY_HOST',
35-
),
3632
useBedrock: await beneath.env.get('CLAUDE_CODE_USE_BEDROCK'),
3733
useVertex: await beneath.env.get('CLAUDE_CODE_USE_VERTEX'),
3834
useFoundry: await beneath.env.get('CLAUDE_CODE_USE_FOUNDRY'),

‎mods/telemetry/hooks/telemetry-deps/telemetry-deps.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ export type TelemetryDeps = {
2323
model: () => Promise<string>
2424

2525
/**
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.
2828
*/
2929
environment: () => Promise<Environment>
3030

‎mods/telemetry/hooks/telemetry-of/telemetry-of.ts‎

Lines changed: 26 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -4,60 +4,43 @@ import type { TelemetryDeps } from '../telemetry-deps'
44
import type TelemetryTypes from '../telemetry-types'
55

66
/**
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.
99
*
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.
1313
*
1414
* @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`
1716
*/
1817
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()
2119

2220
async function post(
2321
fields: Entries.Fields,
2422
method: TelemetryTypes.Method,
2523
): Promise<void> {
26-
read ??= deps.environment().catch((error: unknown) => {
27-
read = undefined
28-
29-
throw error
30-
})
31-
const environment = await read
24+
const environment = await deps.environment()
3225

3326
if (isAnalyticsOff(environment)) {
3427
return
3528
}
3629

37-
if (!held) {
38-
held = deps.authorize().catch((error: unknown) => {
39-
held = undefined
40-
41-
throw error
42-
})
43-
}
44-
45-
const auth = await held
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()
4636

4737
if (!auth) {
48-
held = undefined
49-
5038
throw Entries.refusal(
5139
'this session has no first-party credential to authorize',
5240
method,
5341
)
5442
}
5543

56-
const body = Entries.batchOf(fields, {
57-
sessionId: await deps.id(),
58-
model: await deps.model(),
59-
userType: environment.userType === 'ant' ? 'ant' : 'external',
60-
})
6144
const response = await deps.fetch(Entries.INGEST_URL, {
6245
method: 'POST',
6346
headers: {
@@ -73,8 +56,18 @@ export function telemetryOf(deps: TelemetryDeps): TelemetryTypes.Telemetry {
7356
}
7457
}
7558

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+
7669
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'),
7972
}
8073
}

0 commit comments

Comments
 (0)