@indigoai-us/hq-cloud 6.14.24 → 6.14.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -33,24 +33,42 @@ interface StubClient extends TelemetryClientSurface {
33
33
  posts: UsageBatch[];
34
34
  /** Number of times `getTelemetryOptIn` was called. */
35
35
  optInCalls: number;
36
+ /** Every value handed to `setTelemetryOptIn`, in arrival order. */
37
+ optInSets: boolean[];
38
+ /** The options object passed alongside each `setTelemetryOptIn` call. */
39
+ optInSetOpts: Array<{ onlyIfUnset?: boolean } | undefined>;
36
40
  }
37
41
 
38
42
  function makeClient(opts: {
39
43
  optInResponse?: TelemetryOptInResponse | Error;
40
44
  postResponse?: UsageIngestResult | Error;
45
+ /** When set, `setTelemetryOptIn` rejects with this error. */
46
+ setOptInError?: Error;
47
+ /** `applied` value the conditional write reports back (default true). */
48
+ setOptInApplied?: boolean;
41
49
  } = {}): StubClient {
42
50
  const optInResponse = opts.optInResponse ?? { enabled: true, updatedAt: null };
43
51
  const postResponse = opts.postResponse ?? { ok: true, written: 0, skipped: [] };
44
52
 
45
53
  const posts: UsageBatch[] = [];
54
+ const optInSets: boolean[] = [];
55
+ const optInSetOpts: Array<{ onlyIfUnset?: boolean } | undefined> = [];
46
56
  const stub: StubClient = {
47
57
  posts,
48
58
  optInCalls: 0,
59
+ optInSets,
60
+ optInSetOpts,
49
61
  async getTelemetryOptIn() {
50
62
  this.optInCalls++;
51
63
  if (optInResponse instanceof Error) throw optInResponse;
52
64
  return optInResponse;
53
65
  },
66
+ async setTelemetryOptIn(enabled: boolean, setOpts?: { onlyIfUnset?: boolean }) {
67
+ optInSets.push(enabled);
68
+ optInSetOpts.push(setOpts);
69
+ if (opts.setOptInError) throw opts.setOptInError;
70
+ return { applied: opts.setOptInApplied ?? true };
71
+ },
54
72
  async postUsage(batch: UsageBatch) {
55
73
  posts.push(batch);
56
74
  if (postResponse instanceof Error) throw postResponse;
@@ -722,3 +740,226 @@ describe("collectAndSendTelemetry — companyUid attribution", () => {
722
740
  for (const ev of events) expect(ev.companyUid).toBe(COMPANY_UID);
723
741
  });
724
742
  });
743
+
744
+ const CALLER = "prs_01CALLER";
745
+
746
+ // ── Consent self-heal ────────────────────────────────────────────────────────
747
+ //
748
+ // Regression for the opt-in race: the installer writes the user's answer to
749
+ // `~/.hq/menubar.json` (always succeeds) AND posts it to `/v1/usage/opt-in` —
750
+ // but that post fires before the person entity exists, so it 404s and the
751
+ // server attribute is never written. Absence read as `false`, the emitter went
752
+ // silent, and the person showed as "not opted in" forever. Measured in prod:
753
+ // 22 of 33 active Indigo members had NO attribute at all, against 2 genuine
754
+ // opt-outs.
755
+ //
756
+ // The heal replays the answer the user already gave. It must fire ONLY on the
757
+ // server's `unset` state, and must never invent or overwrite consent.
758
+
759
+ describe("collectAndSendTelemetry — consent self-heal", () => {
760
+ let env: TestEnv;
761
+ beforeEach(() => {
762
+ env = setupEnv();
763
+ });
764
+ afterEach(() => teardownEnv(env));
765
+
766
+ // The cached answer must name the account it belongs to; an unbound record is
767
+ // deliberately not replayable (see the cross-account test below).
768
+ function writeMenubar(value: unknown, personUid: string | undefined = CALLER): void {
769
+ fs.writeFileSync(
770
+ env.menubarPath,
771
+ JSON.stringify({
772
+ telemetryEnabled: value,
773
+ ...(personUid ? { telemetryOptInPersonUid: personUid } : {}),
774
+ }),
775
+ );
776
+ }
777
+
778
+ it("re-asserts a local opt-IN when the server has no recorded consent", async () => {
779
+ writeMenubar(true);
780
+ const client = makeClient({
781
+ optInResponse: { enabled: false, updatedAt: null, unset: true, personUid: CALLER },
782
+ });
783
+
784
+ const result = await collectAndSendTelemetry(makeOpts(env, client));
785
+
786
+ expect(client.optInSets).toEqual([true]);
787
+ expect(result.enabled).toBe(true);
788
+ expect(result.optInSource).toBe("menubar-reasserted");
789
+ });
790
+
791
+ it("re-asserts a local opt-OUT so the server records the real answer", async () => {
792
+ writeMenubar(false);
793
+ const client = makeClient({
794
+ optInResponse: { enabled: false, updatedAt: null, unset: true, personUid: CALLER },
795
+ });
796
+
797
+ const result = await collectAndSendTelemetry(makeOpts(env, client));
798
+
799
+ expect(client.optInSets).toEqual([false]);
800
+ expect(result.enabled).toBe(false);
801
+ });
802
+
803
+ it("NEVER overwrites an explicit server opt-out", async () => {
804
+ // The user opted in on this machine at some point, then explicitly opted
805
+ // out account-wide. The account-wide answer must win.
806
+ writeMenubar(true);
807
+ const client = makeClient({
808
+ optInResponse: { enabled: false, updatedAt: "2026-06-19T16:11:09.939Z", unset: false },
809
+ });
810
+
811
+ const result = await collectAndSendTelemetry(makeOpts(env, client));
812
+
813
+ expect(client.optInSets).toEqual([]);
814
+ expect(result.enabled).toBe(false);
815
+ expect(result.optInSource).toBe("server");
816
+ });
817
+
818
+ it("does not invent consent when no local answer exists", async () => {
819
+ // No menubar.json at all — we hold no answer, so we assert nothing.
820
+ const client = makeClient({
821
+ optInResponse: { enabled: false, updatedAt: null, unset: true, personUid: CALLER },
822
+ });
823
+
824
+ const result = await collectAndSendTelemetry(makeOpts(env, client));
825
+
826
+ expect(client.optInSets).toEqual([]);
827
+ expect(result.enabled).toBe(false);
828
+ });
829
+
830
+ it("does not invent consent when the local value is not a boolean", async () => {
831
+ writeMenubar("yes");
832
+ const client = makeClient({
833
+ optInResponse: { enabled: false, updatedAt: null, unset: true, personUid: CALLER },
834
+ });
835
+
836
+ const result = await collectAndSendTelemetry(makeOpts(env, client));
837
+
838
+ expect(client.optInSets).toEqual([]);
839
+ expect(result.enabled).toBe(false);
840
+ });
841
+
842
+ it("falls back to the server answer when the re-assert POST fails", async () => {
843
+ writeMenubar(true);
844
+ const client = makeClient({
845
+ optInResponse: { enabled: false, updatedAt: null, unset: true, personUid: CALLER },
846
+ setOptInError: new Error("network down"),
847
+ });
848
+
849
+ const result = await collectAndSendTelemetry(makeOpts(env, client));
850
+
851
+ expect(client.optInSets).toEqual([true]);
852
+ // Non-fatal: the run completes on the server's answer and retries next time.
853
+ expect(result.enabled).toBe(false);
854
+ expect(result.optInSource).toBe("server");
855
+ });
856
+
857
+ it("skips the heal entirely against an older server that omits `unset`", async () => {
858
+ writeMenubar(true);
859
+ const client = makeClient({ optInResponse: { enabled: false, updatedAt: null } });
860
+
861
+ const result = await collectAndSendTelemetry(makeOpts(env, client));
862
+
863
+ expect(client.optInSets).toEqual([]);
864
+ expect(result.optInSource).toBe("server");
865
+ });
866
+ });
867
+
868
+ // The self-heal must be ATOMIC. Reading `unset` and replaying the local answer
869
+ // are two requests; without a server-side condition another device could record
870
+ // a real opt-out in between and the replay would clobber it.
871
+ describe("collectAndSendTelemetry — self-heal is conditional", () => {
872
+ let env: TestEnv;
873
+ beforeEach(() => {
874
+ env = setupEnv();
875
+ });
876
+ afterEach(() => teardownEnv(env));
877
+
878
+ it("asks the server to write ONLY if the consent is still unset", async () => {
879
+ fs.writeFileSync(
880
+ env.menubarPath,
881
+ JSON.stringify({ telemetryEnabled: true, telemetryOptInPersonUid: CALLER }),
882
+ );
883
+ const client = makeClient({
884
+ optInResponse: { enabled: false, updatedAt: null, unset: true, personUid: CALLER },
885
+ });
886
+
887
+ await collectAndSendTelemetry(makeOpts(env, client));
888
+
889
+ expect(client.optInSetOpts[0]).toMatchObject({ onlyIfUnset: true });
890
+ });
891
+
892
+ it("defers to the server when the conditional write loses the race", async () => {
893
+ // A real answer was recorded elsewhere between our GET and our POST.
894
+ fs.writeFileSync(
895
+ env.menubarPath,
896
+ JSON.stringify({ telemetryEnabled: true, telemetryOptInPersonUid: CALLER }),
897
+ );
898
+ const client = makeClient({
899
+ optInResponse: { enabled: false, updatedAt: null, unset: true, personUid: CALLER },
900
+ setOptInApplied: false,
901
+ });
902
+
903
+ const result = await collectAndSendTelemetry(makeOpts(env, client));
904
+
905
+ // The stale local `true` must NOT win over the answer that landed first.
906
+ expect(result.enabled).toBe(false);
907
+ expect(result.optInSource).toBe("server");
908
+ });
909
+ });
910
+
911
+ // The cached consent lives in a per-MACHINE file. If two people sign in under
912
+ // the same OS user it holds whoever answered LAST, so replaying it for a
913
+ // different account would opt in someone who never consented.
914
+ describe("collectAndSendTelemetry — consent replay is account-scoped", () => {
915
+ let env: TestEnv;
916
+ beforeEach(() => {
917
+ env = setupEnv();
918
+ });
919
+ afterEach(() => teardownEnv(env));
920
+
921
+ it("does NOT replay a cached answer belonging to a different account", async () => {
922
+ fs.writeFileSync(
923
+ env.menubarPath,
924
+ JSON.stringify({ telemetryEnabled: true, telemetryOptInPersonUid: "prs_01SOMEONE_ELSE" }),
925
+ );
926
+ const client = makeClient({
927
+ optInResponse: { enabled: false, updatedAt: null, unset: true, personUid: CALLER },
928
+ });
929
+
930
+ const result = await collectAndSendTelemetry(makeOpts(env, client));
931
+
932
+ expect(client.optInSets).toEqual([]);
933
+ expect(result.enabled).toBe(false);
934
+ });
935
+
936
+ it("does NOT replay a legacy record with no account binding", async () => {
937
+ // Written before consent was bound to an account — we cannot prove whose
938
+ // answer it is, so the installer's post-sign-in upload is the recovery path.
939
+ fs.writeFileSync(env.menubarPath, JSON.stringify({ telemetryEnabled: true }));
940
+ const client = makeClient({
941
+ optInResponse: { enabled: false, updatedAt: null, unset: true, personUid: CALLER },
942
+ });
943
+
944
+ const result = await collectAndSendTelemetry(makeOpts(env, client));
945
+
946
+ expect(client.optInSets).toEqual([]);
947
+ expect(result.enabled).toBe(false);
948
+ });
949
+
950
+ it("does NOT replay when the server cannot say who the caller is", async () => {
951
+ fs.writeFileSync(
952
+ env.menubarPath,
953
+ JSON.stringify({ telemetryEnabled: true, telemetryOptInPersonUid: CALLER }),
954
+ );
955
+ // Older server: no `personUid`, so the match cannot be proven.
956
+ const client = makeClient({
957
+ optInResponse: { enabled: false, updatedAt: null, unset: true },
958
+ });
959
+
960
+ const result = await collectAndSendTelemetry(makeOpts(env, client));
961
+
962
+ expect(client.optInSets).toEqual([]);
963
+ expect(result.enabled).toBe(false);
964
+ });
965
+ });
package/src/telemetry.ts CHANGED
@@ -48,6 +48,14 @@ import type {
48
48
  export interface TelemetryClientSurface {
49
49
  getTelemetryOptIn(): Promise<TelemetryOptInResponse>;
50
50
  postUsage(batch: UsageBatch): Promise<UsageIngestResult>;
51
+ /**
52
+ * Optional so an older client (or a narrow test stub) still satisfies the
53
+ * surface — when it is absent the consent self-heal is simply skipped.
54
+ */
55
+ setTelemetryOptIn?(
56
+ enabled: boolean,
57
+ opts?: { onlyIfUnset?: boolean },
58
+ ): Promise<{ applied: boolean } | void>;
51
59
  }
52
60
 
53
61
  export interface CollectTelemetryOptions {
@@ -79,7 +87,7 @@ export interface CollectTelemetryResult {
79
87
  /** Whether the opt-in check resolved to true (either server-side or via the menubar fallback). When false, nothing else ran. */
80
88
  enabled: boolean;
81
89
  /** Source for the `enabled` decision — useful for diagnosing missing-events reports. */
82
- optInSource: "server" | "menubar-fallback" | "skipped";
90
+ optInSource: "server" | "menubar-fallback" | "menubar-reasserted" | "skipped";
83
91
  /** How many `.jsonl` files we considered (before the cursor diff). */
84
92
  filesScanned: number;
85
93
  /** Total events successfully POSTed across all batches. */
@@ -129,12 +137,58 @@ async function saveCursor(cursorPath: string, cursor: TelemetryCursor): Promise<
129
137
  // ── Local opt-in fallback ─────────────────────────────────────────────────────
130
138
 
131
139
  async function readLocalTelemetryEnabled(menubarPath: string): Promise<boolean> {
140
+ return (await readLocalTelemetryPreference(menubarPath)) === true;
141
+ }
142
+
143
+ /**
144
+ * Tri-state read of the locally-stored consent.
145
+ *
146
+ * `~/.hq/menubar.json` → `telemetryEnabled` is written by the installer the
147
+ * moment the user answers the prompt, and that local write ALWAYS succeeds —
148
+ * unlike the paired server write, which fires before the person entity exists
149
+ * and 404s. So this file is frequently the ONLY durable record of the user's
150
+ * actual choice.
151
+ *
152
+ * `undefined` means absent / unreadable / not a boolean — "we hold no answer" —
153
+ * as distinct from `false`, which is a real opt-out. The self-heal path must
154
+ * never conflate the two: it replays an answer, it does not invent one.
155
+ */
156
+ async function readLocalTelemetryPreference(
157
+ menubarPath: string,
158
+ ): Promise<boolean | undefined> {
159
+ return (await readLocalConsentRecord(menubarPath)).enabled;
160
+ }
161
+
162
+ /**
163
+ * The locally-cached consent plus the account it belongs to.
164
+ *
165
+ * `telemetryOptInPersonUid` binds the answer to the `prs_*` that gave it.
166
+ * `menubar.json` is a per-MACHINE file, so when two people sign in under the
167
+ * same OS user it holds whoever answered LAST — replaying it unconditionally
168
+ * would opt in an account that never consented. The binding is what makes the
169
+ * replay safe; an unbound (legacy) record cannot be proven to belong to the
170
+ * current caller and is therefore never replayed.
171
+ */
172
+ async function readLocalConsentRecord(
173
+ menubarPath: string,
174
+ ): Promise<{ enabled?: boolean; personUid?: string }> {
132
175
  try {
133
176
  const raw = await fs.readFile(menubarPath, "utf-8");
134
- const parsed = JSON.parse(raw) as { telemetryEnabled?: unknown };
135
- return parsed.telemetryEnabled === true;
177
+ const parsed = JSON.parse(raw) as {
178
+ telemetryEnabled?: unknown;
179
+ telemetryOptInPersonUid?: unknown;
180
+ };
181
+ return {
182
+ enabled:
183
+ typeof parsed.telemetryEnabled === "boolean" ? parsed.telemetryEnabled : undefined,
184
+ personUid:
185
+ typeof parsed.telemetryOptInPersonUid === "string" &&
186
+ parsed.telemetryOptInPersonUid.length > 0
187
+ ? parsed.telemetryOptInPersonUid
188
+ : undefined,
189
+ };
136
190
  } catch {
137
- return false;
191
+ return {};
138
192
  }
139
193
  }
140
194
 
@@ -422,15 +476,79 @@ export async function collectAndSendTelemetry(
422
476
  // When `hqRoot` is omitted the map is empty → every event stays unattributed.
423
477
  const repoCompanyMap: RepoCompanyMap = opts.hqRoot
424
478
  ? await buildRepoCompanyMap(opts.hqRoot)
425
- : { entries: [], bySlug: new Map() };
479
+ : { entries: [], bySlug: new Map(), foldsCase: false, ambiguous: new Set<string>() };
426
480
 
427
- // 1. Opt-in check (server-authoritative, with local fallback).
481
+ // 1. Opt-in check (server-authoritative, with local fallback + self-heal).
428
482
  let enabled: boolean;
429
483
  let optInSource: CollectTelemetryResult["optInSource"];
430
484
  try {
431
485
  const resp = await opts.client.getTelemetryOptIn();
432
486
  enabled = resp.enabled === true;
433
487
  optInSource = "server";
488
+
489
+ // Self-heal a consent the user gave but that never reached the server.
490
+ //
491
+ // The installer writes the answer to `~/.hq/menubar.json` (always succeeds)
492
+ // AND posts it to `/v1/usage/opt-in` — but that post fires before the
493
+ // person entity exists, so it 404s and the attribute is never written.
494
+ // Absence then reads as `false`, the emitter goes silent, and the person
495
+ // shows as "not opted in" forever. Measured: 22 of 33 active Indigo members
496
+ // had no attribute at all, against only 2 genuine opt-outs.
497
+ //
498
+ // `unset` (never answered) is the ONLY state we heal. An explicit server
499
+ // `false` is a real opt-out and is left strictly alone. We also require a
500
+ // local answer to actually exist — we never invent consent, we only replay
501
+ // the answer the user already gave.
502
+ //
503
+ // And it must be THIS account's answer. `menubar.json` is per-MACHINE, so
504
+ // when two people sign in under the same OS user it holds whoever answered
505
+ // last; replaying that for the second account would opt in someone who
506
+ // never consented. The replay therefore requires the cached record to name
507
+ // the same `prs_*` the server says we are. A legacy record with no binding
508
+ // cannot be proven to belong to this caller and is skipped — for those
509
+ // machines the installer's own post-sign-in upload is the recovery path,
510
+ // and it is correctly account-scoped because it runs right after that
511
+ // person authenticates.
512
+ if (resp.unset === true && opts.client.setTelemetryOptIn) {
513
+ const record = await readLocalConsentRecord(menubarPath);
514
+ const local = record.enabled;
515
+ const boundToCaller =
516
+ record.personUid !== undefined &&
517
+ resp.personUid !== undefined &&
518
+ record.personUid === resp.personUid;
519
+ if (local !== undefined && !boundToCaller) {
520
+ log(
521
+ "[telemetry] skipping consent re-assert: the locally cached answer is not bound to the signed-in account",
522
+ );
523
+ }
524
+ if (local !== undefined && boundToCaller) {
525
+ try {
526
+ // `onlyIfUnset` keeps the replay atomic. Reading `unset` and writing
527
+ // are two requests, so without it another device could record a real
528
+ // opt-out in between and this stale local answer would overwrite it.
529
+ // The server tests "still unset?" as part of the write instead.
530
+ const ack = await opts.client.setTelemetryOptIn(local, { onlyIfUnset: true });
531
+ if (ack && ack.applied === false) {
532
+ // Lost the race — a real answer was recorded first and it stands.
533
+ // Keep the server's answer for this run rather than the local one.
534
+ log(
535
+ "[telemetry] consent was recorded elsewhere before the re-assert landed; deferring to the server",
536
+ );
537
+ } else {
538
+ enabled = local;
539
+ optInSource = "menubar-reasserted";
540
+ log(
541
+ `[telemetry] server had no recorded consent; re-asserted the local install-time answer (enabled=${local})`,
542
+ );
543
+ }
544
+ } catch (err) {
545
+ // Non-fatal: fall through on the server's answer. Next run retries.
546
+ log(
547
+ `[telemetry] failed to re-assert local opt-in (${(err as Error).message ?? err})`,
548
+ );
549
+ }
550
+ }
551
+ }
434
552
  } catch (err) {
435
553
  log(`[telemetry] opt-in check failed (${(err as Error).message ?? err}) — falling back to local menubar.json`);
436
554
  enabled = await readLocalTelemetryEnabled(menubarPath);
@@ -414,6 +414,25 @@ export interface VendChildResult {
414
414
  export interface TelemetryOptInResponse {
415
415
  enabled: boolean;
416
416
  updatedAt: string | null;
417
+ /**
418
+ * `true` when the person row carries NO `telemetryOptIn` attribute — i.e. the
419
+ * consent question has never been answered server-side. Distinct from
420
+ * `enabled: false`, which is a deliberate opt-OUT.
421
+ *
422
+ * Optional because older servers omit it entirely. Absent is treated as
423
+ * `false`, so a client talking to one behaves exactly as before — no
424
+ * self-heal, no surprise writes.
425
+ */
426
+ unset?: boolean;
427
+ /**
428
+ * The `prs_*` uid this answer belongs to — i.e. the authenticated caller.
429
+ *
430
+ * Needed because the local consent cache is a per-MACHINE file: if two people
431
+ * sign in under the same OS user it holds whoever answered last. A client
432
+ * must not replay it for a different account. Optional (older servers omit
433
+ * it), and absence means the replay cannot be proven safe, so it is skipped.
434
+ */
435
+ personUid?: string;
417
436
  }
418
437
 
419
438
  export interface UsageBatch {
@@ -637,9 +656,26 @@ const telemetryOptInResponseSchema: VaultResponseSchema<TelemetryOptInResponse>
637
656
  .object({
638
657
  enabled: z.boolean().default(false),
639
658
  updatedAt: z.string().nullable().default(null),
659
+ personUid: z.string().optional(),
660
+ // `.optional()`, NOT `.default(false)`: an older server omits this field,
661
+ // and materializing it would change the parsed response shape for every
662
+ // legacy caller. Callers test `unset === true`, so absent reads as
663
+ // "not unset" and the self-heal path is skipped, which is the pre-existing
664
+ // behaviour exactly.
665
+ unset: z.boolean().optional(),
640
666
  })
641
667
  .strip();
642
668
 
669
+ /**
670
+ * `POST /v1/usage/opt-in` acknowledgement.
671
+ *
672
+ * `applied` is absent on older servers (which always wrote unconditionally), so
673
+ * it stays optional and callers treat absence as "the write landed".
674
+ */
675
+ const telemetryOptInAckSchema: VaultResponseSchema<{ ok: boolean; applied?: boolean }> = z
676
+ .object({ ok: z.boolean().default(true), applied: z.boolean().optional() })
677
+ .strip();
678
+
643
679
  const usageIngestResultSchema: VaultResponseSchema<UsageIngestResult> = z
644
680
  .object({
645
681
  ok: z.boolean(),
@@ -1353,6 +1389,38 @@ export class VaultClient {
1353
1389
  return this.get("/v1/usage/opt-in", telemetryOptInResponseSchema);
1354
1390
  }
1355
1391
 
1392
+ /**
1393
+ * `POST /v1/usage/opt-in` — record the authenticated caller's consent.
1394
+ *
1395
+ * The installer is the primary writer (it owns the consent prompt). This
1396
+ * client-side setter exists so the sync runner can RE-ASSERT a consent the
1397
+ * user already gave locally but which never reached the server: the
1398
+ * installer's write fires before the person entity exists and 404s on
1399
+ * `no-person-entity`, so the answer survives only in `~/.hq/menubar.json`.
1400
+ * See `./telemetry.ts::collectAndSendTelemetry`, which calls this ONLY when
1401
+ * the server reports `unset` — never over an explicit opt-out.
1402
+ *
1403
+ * `onlyIfUnset` makes the server write conditional on the consent still never
1404
+ * having been recorded. The self-heal MUST pass it: reading `unset` and
1405
+ * replaying the answer are two separate requests, so without the condition
1406
+ * another device could record a real opt-out in between and this replay would
1407
+ * silently overwrite it. A deliberate user choice omits the flag so it always
1408
+ * wins. The response's `applied` reports whether the write landed.
1409
+ */
1410
+ async setTelemetryOptIn(
1411
+ enabled: boolean,
1412
+ opts?: { onlyIfUnset?: boolean },
1413
+ ): Promise<{ applied: boolean }> {
1414
+ const ack = await this.post(
1415
+ "/v1/usage/opt-in",
1416
+ opts?.onlyIfUnset ? { enabled, onlyIfUnset: true } : { enabled },
1417
+ telemetryOptInAckSchema,
1418
+ );
1419
+ // Older servers answer `{ ok: true }` with no `applied`; they also write
1420
+ // unconditionally, so a 2xx there means the write did land.
1421
+ return { applied: ack.applied ?? true };
1422
+ }
1423
+
1356
1424
  /**
1357
1425
  * `POST /v1/usage` — upload a batch of sanitized telemetry events.
1358
1426
  *