diff --git a/packages/sdk/telemetry/README.md b/packages/sdk/telemetry/README.md index 3a8b6ac4c2..43d13d3dcc 100644 --- a/packages/sdk/telemetry/README.md +++ b/packages/sdk/telemetry/README.md @@ -10,7 +10,7 @@ Launcher-side telemetry primitives for the dsh-sdk toolchain. This is a plain li | `getOrCreateAnonymousId` | Random UUID persisted in a per-user GLOBAL config file (never in the project, never derived from git). | | `TelemetryReporter` | Fire-and-forget send: `report()` never blocks or throws; delivery resolves on every path; `flush()` optionally drains in-flight sends within a cap. | -Consent is carried by the telemetry entry in `cordis.yml`, so disabling telemetry is disabling that entry. When `cordis.yml` does not yet exist (first `create`) consent defaults to allowed; when it exists without a telemetry entry consent defaults to denied — both are configurable on `ConsentResolver`. +Consent is carried by the telemetry entry in `cordis.yml`, so disabling telemetry is disabling that entry. Telemetry reports by default and is off only when a present telemetry entry is explicitly `disabled`: a missing `cordis.yml` (first `create`), an enabled entry, or a `cordis.yml` with no telemetry entry all report. `DO_NOT_TRACK`/CI always deny. The no-config and absent-entry defaults are configurable on `ConsentResolver`. The collection endpoint is a fixed constant (`DSH_TELEMETRY_ENDPOINT`); its `.invalid` placeholder must be replaced with the real endpoint before release. diff --git a/packages/sdk/telemetry/src/consent-resolver.ts b/packages/sdk/telemetry/src/consent-resolver.ts index 1d50904f39..a4327dc9f7 100644 --- a/packages/sdk/telemetry/src/consent-resolver.ts +++ b/packages/sdk/telemetry/src/consent-resolver.ts @@ -1,12 +1,12 @@ /** * Consent resolution for dsh-sdk telemetry. * - * Consent is carried by the telemetry plugin's enabled/disabled state in the - * project `cordis.yml`: an enabled entry means opt-in, a `disabled: true` entry - * means opt-out. The resolver PARSES `cordis.yml` — it never boots a Cordis - * application — because several launcher commands (`build`, `create`) never - * boot Cordis at all. `DO_NOT_TRACK` and CI environment signals force a denial - * regardless of file state. + * Telemetry is OFF only when `cordis.yml` contains a telemetry entry that is + * explicitly `disabled`; every other file state reports (no `cordis.yml`, an + * enabled entry, or no telemetry entry at all). The resolver PARSES `cordis.yml` + * — it never boots a Cordis application — because several launcher commands + * (`build`, `create`) never boot Cordis at all. `DO_NOT_TRACK` and CI + * environment signals force a denial regardless of file state. * * @module @deepseek-ai/dsh-telemetry/consent-resolver */ @@ -56,7 +56,7 @@ export interface ConsentResolverOptions { honorEnvOptOut?: boolean /** Consent when `cordis.yml` does not exist yet (first `create`). Defaults to `true` (telemetry is default-on). */ allowWhenNoConfig?: boolean - /** Consent when `cordis.yml` exists but has no telemetry entry. Defaults to `false`. */ + /** Consent when `cordis.yml` exists but has no telemetry entry. Defaults to `true` (report unless a present entry is disabled). */ allowWhenEntryAbsent?: boolean } @@ -94,7 +94,7 @@ export class ConsentResolver { this.#env = options.env ?? process.env this.#honorEnvOptOut = options.honorEnvOptOut ?? true this.#allowWhenNoConfig = options.allowWhenNoConfig ?? true - this.#allowWhenEntryAbsent = options.allowWhenEntryAbsent ?? false + this.#allowWhenEntryAbsent = options.allowWhenEntryAbsent ?? true } /** diff --git a/packages/sdk/telemetry/tests/consent-resolver.spec.ts b/packages/sdk/telemetry/tests/consent-resolver.spec.ts index e1f5f90f81..ca0cec3bbd 100644 --- a/packages/sdk/telemetry/tests/consent-resolver.spec.ts +++ b/packages/sdk/telemetry/tests/consent-resolver.spec.ts @@ -84,23 +84,23 @@ describe('ConsentResolver cordis.yml state', () => { .toEqual({ allowed: true, reason: 'enabled' }) }) - it('reports absent when cordis.yml has no telemetry entry, defaulting to deny', async () => { + it('reports (allows) when cordis.yml has no telemetry entry', async () => { const yml = '- id: llm\n name: \'@deepseek-ai/dsh-llm-deepseek\'\n' expect(await resolver.resolve(await projectDir(yml))) - .toEqual({ allowed: false, reason: 'absent' }) + .toEqual({ allowed: true, reason: 'absent' }) }) - it('can allow when the entry is absent', async () => { + it('can be told to deny when the entry is absent', async () => { const yml = '- id: llm\n name: \'@deepseek-ai/dsh-llm-deepseek\'\n' - const decision = await new ConsentResolver({ env: {}, allowWhenEntryAbsent: true }).resolve(await projectDir(yml)) - expect(decision).toEqual({ allowed: true, reason: 'absent' }) + const decision = await new ConsentResolver({ env: {}, allowWhenEntryAbsent: false }).resolve(await projectDir(yml)) + expect(decision).toEqual({ allowed: false, reason: 'absent' }) }) - it('skips non-object sequence items and a non-sequence root', async () => { + it('skips non-object sequence items and a non-sequence root, still reporting absent', async () => { expect(await resolver.resolve(await projectDir('- just-a-string\n- id: x\n name: y\n'))) - .toEqual({ allowed: false, reason: 'absent' }) + .toEqual({ allowed: true, reason: 'absent' }) expect(await resolver.resolve(await projectDir('root: not-a-sequence\n'))) - .toEqual({ allowed: false, reason: 'absent' }) + .toEqual({ allowed: true, reason: 'absent' }) }) it('honors a custom telemetry plugin name', async () => {