@indigoai-us/hq-cloud 6.18.97 → 6.18.99

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.
Files changed (87) hide show
  1. package/dist/bin/sync-runner-company.d.ts.map +1 -1
  2. package/dist/bin/sync-runner-company.js +32 -3
  3. package/dist/bin/sync-runner-company.js.map +1 -1
  4. package/dist/bin/sync-runner-watch-loop.d.ts.map +1 -1
  5. package/dist/bin/sync-runner-watch-loop.js +50 -3
  6. package/dist/bin/sync-runner-watch-loop.js.map +1 -1
  7. package/dist/bin/sync-runner.d.ts +3 -0
  8. package/dist/bin/sync-runner.d.ts.map +1 -1
  9. package/dist/bin/sync-runner.js +1 -0
  10. package/dist/bin/sync-runner.js.map +1 -1
  11. package/dist/cli/share.d.ts +5 -0
  12. package/dist/cli/share.d.ts.map +1 -1
  13. package/dist/cli/share.js +120 -15
  14. package/dist/cli/share.js.map +1 -1
  15. package/dist/cli/sync.d.ts +18 -2
  16. package/dist/cli/sync.d.ts.map +1 -1
  17. package/dist/cli/sync.js +55 -4
  18. package/dist/cli/sync.js.map +1 -1
  19. package/dist/index.d.ts +2 -2
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js +1 -1
  22. package/dist/index.js.map +1 -1
  23. package/dist/lib/sync-leg-error-summary.d.ts +9 -0
  24. package/dist/lib/sync-leg-error-summary.d.ts.map +1 -0
  25. package/dist/lib/sync-leg-error-summary.js +200 -0
  26. package/dist/lib/sync-leg-error-summary.js.map +1 -0
  27. package/dist/object-io.d.ts +2 -3
  28. package/dist/object-io.d.ts.map +1 -1
  29. package/dist/object-io.js +15 -4
  30. package/dist/object-io.js.map +1 -1
  31. package/dist/outcome-telemetry.d.ts +2 -1
  32. package/dist/outcome-telemetry.d.ts.map +1 -1
  33. package/dist/outcome-telemetry.js +13 -12
  34. package/dist/outcome-telemetry.js.map +1 -1
  35. package/dist/plan-limit-pause.d.ts +19 -3
  36. package/dist/plan-limit-pause.d.ts.map +1 -1
  37. package/dist/plan-limit-pause.js +80 -10
  38. package/dist/plan-limit-pause.js.map +1 -1
  39. package/dist/plan-limit.d.ts +51 -5
  40. package/dist/plan-limit.d.ts.map +1 -1
  41. package/dist/plan-limit.js +79 -5
  42. package/dist/plan-limit.js.map +1 -1
  43. package/dist/s3.d.ts +2 -0
  44. package/dist/s3.d.ts.map +1 -1
  45. package/dist/s3.js +4 -2
  46. package/dist/s3.js.map +1 -1
  47. package/dist/script-eol.d.ts +4 -2
  48. package/dist/script-eol.d.ts.map +1 -1
  49. package/dist/script-eol.js +26 -2
  50. package/dist/script-eol.js.map +1 -1
  51. package/dist/skill-telemetry.d.ts +10 -1
  52. package/dist/skill-telemetry.d.ts.map +1 -1
  53. package/dist/skill-telemetry.js +101 -16
  54. package/dist/skill-telemetry.js.map +1 -1
  55. package/dist/sync-object-events.d.ts +61 -0
  56. package/dist/sync-object-events.d.ts.map +1 -0
  57. package/dist/sync-object-events.js +138 -0
  58. package/dist/sync-object-events.js.map +1 -0
  59. package/dist/sync-pass-event.d.ts +45 -0
  60. package/dist/sync-pass-event.d.ts.map +1 -0
  61. package/dist/sync-pass-event.js +102 -0
  62. package/dist/sync-pass-event.js.map +1 -0
  63. package/dist/sync-request-counter.d.ts +13 -0
  64. package/dist/sync-request-counter.d.ts.map +1 -0
  65. package/dist/sync-request-counter.js +29 -0
  66. package/dist/sync-request-counter.js.map +1 -0
  67. package/dist/telemetry/consent-source.d.ts +68 -0
  68. package/dist/telemetry/consent-source.d.ts.map +1 -0
  69. package/dist/telemetry/consent-source.js +87 -0
  70. package/dist/telemetry/consent-source.js.map +1 -0
  71. package/dist/telemetry/pre-consent-buffer.d.ts +55 -0
  72. package/dist/telemetry/pre-consent-buffer.d.ts.map +1 -0
  73. package/dist/telemetry/pre-consent-buffer.js +303 -0
  74. package/dist/telemetry/pre-consent-buffer.js.map +1 -0
  75. package/dist/telemetry.d.ts +10 -1
  76. package/dist/telemetry.d.ts.map +1 -1
  77. package/dist/telemetry.js +171 -65
  78. package/dist/telemetry.js.map +1 -1
  79. package/dist/test-fixtures/noop-sync-object-events.d.ts +6 -0
  80. package/dist/test-fixtures/noop-sync-object-events.d.ts.map +1 -0
  81. package/dist/test-fixtures/noop-sync-object-events.js +22 -0
  82. package/dist/test-fixtures/noop-sync-object-events.js.map +1 -0
  83. package/dist/vault-client.d.ts +25 -0
  84. package/dist/vault-client.d.ts.map +1 -1
  85. package/dist/vault-client.js +15 -0
  86. package/dist/vault-client.js.map +1 -1
  87. package/package.json +1 -1
@@ -0,0 +1,68 @@
1
+ /**
2
+ * One consent decision shared by the usage, skill and outcome collectors.
3
+ *
4
+ * `GET /v1/usage/opt-in` returns `enabled` (authoritative) plus a
5
+ * `consentSource` that names the rule that decided it. The server also returns
6
+ * a legacy `source` field, which is stored provenance (`"cli"`, `"menubar"`,
7
+ * `null`) and is never read as the rule here. This module turns that answer, or the lack of one, into what
8
+ * a collector should do with personal (unattributed) rows this run. Company
9
+ * rows are not decided here: they keep shipping under each collector's
10
+ * existing consent split whenever personal rows are off.
11
+ *
12
+ * Decision table (first match wins):
13
+ *
14
+ * | Input (`consentSource`) | collect | optInSource | buffer | discardBuffer | localMayOverride |
15
+ * |-----------------------------------------|--------------|--------------------|--------|---------------|------------------|
16
+ * | forceCollect (fleet agent) | true | agent-required | no | no | no |
17
+ * | GET failed | local===true | menubar-fallback | no | no | no |
18
+ * | notice-pending | false | notice-pending | yes | no | no |
19
+ * | plan-required | enabled | plan-required | no | if !enabled | no |
20
+ * | default-on | enabled | default-on | no | if !enabled | no |
21
+ * | personal-toggle | enabled | personal-toggle | no | if !enabled | yes |
22
+ * | agent-required (server says so) | enabled | agent-required | no | if !enabled | no |
23
+ * | legacy-opt-in / missing / other | enabled | server | no | if !enabled | yes |
24
+ *
25
+ * `localMayOverride` gates the existing `unset` self-heal in `telemetry.ts`:
26
+ * only a personal-choice answer may be replaced by the locally cached answer,
27
+ * so a required plan can never be turned off by the menubar preference.
28
+ *
29
+ * `discardBuffer` is set whenever the server answers `enabled: false` for any
30
+ * source other than notice-pending: rows held during a notice period are only
31
+ * ever delivered while that account's collection is on.
32
+ */
33
+ import type { TelemetryOptInResponse } from "../vault-client.js";
34
+ export type TelemetryConsentSource = "plan-required" | "notice-pending" | "default-on" | "personal-toggle" | "agent-required" | "legacy-opt-in";
35
+ /**
36
+ * Collector-facing label for where the decision came from. `"server"` is kept
37
+ * for legacy answers so existing dashboards and tests read the same as before.
38
+ */
39
+ export type DecidedOptInSource = "server" | "menubar-fallback" | "agent-required" | "plan-required" | "notice-pending" | "default-on" | "personal-toggle";
40
+ export interface CollectionDecision {
41
+ /** Ship personal / unattributed rows this run. */
42
+ collect: boolean;
43
+ optInSource: DecidedOptInSource;
44
+ /** Hold personal rows in the account's pre-consent buffer instead of dropping them. */
45
+ buffer: boolean;
46
+ /** The person refused: delete the account's pre-consent buffer unsent. */
47
+ discardBuffer: boolean;
48
+ /** The `unset` self-heal may replay the local menubar answer. */
49
+ localMayOverride: boolean;
50
+ /** The normalised server source, when the server answered. */
51
+ source?: TelemetryConsentSource;
52
+ /** Short human-readable explanation for logs. */
53
+ reason: string;
54
+ }
55
+ export interface LocalConsentInput {
56
+ /** Fleet agents always collect; the GET is never made. */
57
+ forceCollect?: boolean;
58
+ /** `~/.hq/menubar.json` `telemetryEnabled`; read only when the GET failed. */
59
+ localEnabled?: boolean;
60
+ }
61
+ /** Absent or unrecognised `consentSource` → `"legacy-opt-in"` (today's behaviour). */
62
+ export declare function normalizeConsentSource(source: string | null | undefined): TelemetryConsentSource;
63
+ /**
64
+ * @param resp the server answer, or `undefined` when the GET failed (or was
65
+ * skipped because `forceCollect` is set).
66
+ */
67
+ export declare function decideCollection(resp: TelemetryOptInResponse | undefined, local: LocalConsentInput): CollectionDecision;
68
+ //# sourceMappingURL=consent-source.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"consent-source.d.ts","sourceRoot":"","sources":["../../src/telemetry/consent-source.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AAEjE,MAAM,MAAM,sBAAsB,GAC9B,eAAe,GACf,gBAAgB,GAChB,YAAY,GACZ,iBAAiB,GACjB,gBAAgB,GAChB,eAAe,CAAC;AAWpB;;;GAGG;AACH,MAAM,MAAM,kBAAkB,GAC1B,QAAQ,GACR,kBAAkB,GAClB,gBAAgB,GAChB,eAAe,GACf,gBAAgB,GAChB,YAAY,GACZ,iBAAiB,CAAC;AAEtB,MAAM,WAAW,kBAAkB;IACjC,kDAAkD;IAClD,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,kBAAkB,CAAC;IAChC,uFAAuF;IACvF,MAAM,EAAE,OAAO,CAAC;IAChB,0EAA0E;IAC1E,aAAa,EAAE,OAAO,CAAC;IACvB,iEAAiE;IACjE,gBAAgB,EAAE,OAAO,CAAC;IAC1B,8DAA8D;IAC9D,MAAM,CAAC,EAAE,sBAAsB,CAAC;IAChC,iDAAiD;IACjD,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,iBAAiB;IAChC,0DAA0D;IAC1D,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,8EAA8E;IAC9E,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED,sFAAsF;AACtF,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAChC,sBAAsB,CAIxB;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,sBAAsB,GAAG,SAAS,EACxC,KAAK,EAAE,iBAAiB,GACvB,kBAAkB,CAoEpB"}
@@ -0,0 +1,87 @@
1
+ const KNOWN_SOURCES = new Set([
2
+ "plan-required",
3
+ "notice-pending",
4
+ "default-on",
5
+ "personal-toggle",
6
+ "agent-required",
7
+ "legacy-opt-in",
8
+ ]);
9
+ /** Absent or unrecognised `consentSource` → `"legacy-opt-in"` (today's behaviour). */
10
+ export function normalizeConsentSource(source) {
11
+ return typeof source === "string" && KNOWN_SOURCES.has(source)
12
+ ? source
13
+ : "legacy-opt-in";
14
+ }
15
+ /**
16
+ * @param resp the server answer, or `undefined` when the GET failed (or was
17
+ * skipped because `forceCollect` is set).
18
+ */
19
+ export function decideCollection(resp, local) {
20
+ if (local.forceCollect) {
21
+ return {
22
+ collect: true,
23
+ optInSource: "agent-required",
24
+ buffer: false,
25
+ discardBuffer: false,
26
+ localMayOverride: false,
27
+ reason: "fleet agent: collection is always on",
28
+ };
29
+ }
30
+ if (resp === undefined) {
31
+ return {
32
+ collect: local.localEnabled === true,
33
+ optInSource: "menubar-fallback",
34
+ buffer: false,
35
+ discardBuffer: false,
36
+ localMayOverride: false,
37
+ reason: "opt-in check failed: using the local menubar preference",
38
+ };
39
+ }
40
+ const enabled = resp.enabled === true;
41
+ // The rule is `consentSource`. The legacy `source` field is provenance only.
42
+ const source = normalizeConsentSource(resp.consentSource);
43
+ const base = { buffer: false, discardBuffer: !enabled, localMayOverride: false, source };
44
+ switch (source) {
45
+ case "notice-pending":
46
+ return {
47
+ ...base,
48
+ collect: false,
49
+ optInSource: "notice-pending",
50
+ buffer: true,
51
+ discardBuffer: false,
52
+ reason: "plan notice pending: personal rows are held, not sent",
53
+ };
54
+ case "plan-required":
55
+ case "default-on":
56
+ return {
57
+ ...base,
58
+ collect: enabled,
59
+ optInSource: source,
60
+ reason: `${source}: the local preference cannot turn collection off`,
61
+ };
62
+ case "agent-required":
63
+ return {
64
+ ...base,
65
+ collect: enabled,
66
+ optInSource: "agent-required",
67
+ reason: "server reports an agent identity",
68
+ };
69
+ case "personal-toggle":
70
+ return {
71
+ ...base,
72
+ collect: enabled,
73
+ optInSource: "personal-toggle",
74
+ localMayOverride: true,
75
+ reason: enabled ? "personal opt-in" : "personal opt-out",
76
+ };
77
+ case "legacy-opt-in":
78
+ return {
79
+ ...base,
80
+ collect: enabled,
81
+ optInSource: "server",
82
+ localMayOverride: true,
83
+ reason: "legacy opt-in answer",
84
+ };
85
+ }
86
+ }
87
+ //# sourceMappingURL=consent-source.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"consent-source.js","sourceRoot":"","sources":["../../src/telemetry/consent-source.ts"],"names":[],"mappings":"AA0CA,MAAM,aAAa,GAAwB,IAAI,GAAG,CAAyB;IACzE,eAAe;IACf,gBAAgB;IAChB,YAAY;IACZ,iBAAiB;IACjB,gBAAgB;IAChB,eAAe;CAChB,CAAC,CAAC;AAsCH,sFAAsF;AACtF,MAAM,UAAU,sBAAsB,CACpC,MAAiC;IAEjC,OAAO,OAAO,MAAM,KAAK,QAAQ,IAAI,aAAa,CAAC,GAAG,CAAC,MAAM,CAAC;QAC5D,CAAC,CAAE,MAAiC;QACpC,CAAC,CAAC,eAAe,CAAC;AACtB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAC9B,IAAwC,EACxC,KAAwB;IAExB,IAAI,KAAK,CAAC,YAAY,EAAE,CAAC;QACvB,OAAO;YACL,OAAO,EAAE,IAAI;YACb,WAAW,EAAE,gBAAgB;YAC7B,MAAM,EAAE,KAAK;YACb,aAAa,EAAE,KAAK;YACpB,gBAAgB,EAAE,KAAK;YACvB,MAAM,EAAE,sCAAsC;SAC/C,CAAC;IACJ,CAAC;IACD,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACvB,OAAO;YACL,OAAO,EAAE,KAAK,CAAC,YAAY,KAAK,IAAI;YACpC,WAAW,EAAE,kBAAkB;YAC/B,MAAM,EAAE,KAAK;YACb,aAAa,EAAE,KAAK;YACpB,gBAAgB,EAAE,KAAK;YACvB,MAAM,EAAE,yDAAyD;SAClE,CAAC;IACJ,CAAC;IAED,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,KAAK,IAAI,CAAC;IACtC,6EAA6E;IAC7E,MAAM,MAAM,GAAG,sBAAsB,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;IAC1D,MAAM,IAAI,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,EAAE,CAAC,OAAO,EAAE,gBAAgB,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;IACzF,QAAQ,MAAM,EAAE,CAAC;QACf,KAAK,gBAAgB;YACnB,OAAO;gBACL,GAAG,IAAI;gBACP,OAAO,EAAE,KAAK;gBACd,WAAW,EAAE,gBAAgB;gBAC7B,MAAM,EAAE,IAAI;gBACZ,aAAa,EAAE,KAAK;gBACpB,MAAM,EAAE,uDAAuD;aAChE,CAAC;QACJ,KAAK,eAAe,CAAC;QACrB,KAAK,YAAY;YACf,OAAO;gBACL,GAAG,IAAI;gBACP,OAAO,EAAE,OAAO;gBAChB,WAAW,EAAE,MAAM;gBACnB,MAAM,EAAE,GAAG,MAAM,mDAAmD;aACrE,CAAC;QACJ,KAAK,gBAAgB;YACnB,OAAO;gBACL,GAAG,IAAI;gBACP,OAAO,EAAE,OAAO;gBAChB,WAAW,EAAE,gBAAgB;gBAC7B,MAAM,EAAE,kCAAkC;aAC3C,CAAC;QACJ,KAAK,iBAAiB;YACpB,OAAO;gBACL,GAAG,IAAI;gBACP,OAAO,EAAE,OAAO;gBAChB,WAAW,EAAE,iBAAiB;gBAC9B,gBAAgB,EAAE,IAAI;gBACtB,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,kBAAkB;aACzD,CAAC;QACJ,KAAK,eAAe;YAClB,OAAO;gBACL,GAAG,IAAI;gBACP,OAAO,EAAE,OAAO;gBAChB,WAAW,EAAE,QAAQ;gBACrB,gBAAgB,EAAE,IAAI;gBACtB,MAAM,EAAE,sBAAsB;aAC/B,CAAC;IACN,CAAC;AACH,CAAC"}
@@ -0,0 +1,55 @@
1
+ export declare const PRE_CONSENT_BUFFER_MAX_BYTES: number;
2
+ /** A lock untouched for this long is treated as abandoned and reclaimed. */
3
+ export declare const PRE_CONSENT_LOCK_STALE_MS: number;
4
+ /** How long append / flush / discard wait for the lock before giving up. */
5
+ export declare const PRE_CONSENT_LOCK_WAIT_MS: number;
6
+ export interface PreConsentLockOptions {
7
+ staleMs?: number;
8
+ waitMs?: number;
9
+ retryMs?: number;
10
+ }
11
+ /** The buffer lock was still held by another process when the wait ran out. */
12
+ export declare class PreConsentLockTimeoutError extends Error {
13
+ constructor(lockPath: string, waitMs: number);
14
+ }
15
+ export type PreConsentStream = "usage" | "skill";
16
+ type Row = Record<string, unknown>;
17
+ /** Buffer file for this account and stream, or `undefined` when the uid is absent or unsafe. */
18
+ export declare function preConsentBufferPath(bufferDir: string, personUid: string | undefined, stream?: PreConsentStream): string | undefined;
19
+ /**
20
+ * Split rows into batches the server accepts: at most `maxEvents` rows and a
21
+ * serialized `events` array of at most `maxBytes`. Rows are already bounded
22
+ * to fit a batch by the collectors.
23
+ */
24
+ export declare function chunkRows(rows: Row[], maxEvents: number, maxBytes: number): Row[][];
25
+ export interface AppendResult {
26
+ batchesAppended: number;
27
+ /** Oldest batches removed to stay under the cap (may include new ones if a single append exceeds it). */
28
+ batchesDropped: number;
29
+ }
30
+ /** Append batches for `personUid`, then drop the oldest until the file fits `maxBytes`. */
31
+ export declare function appendPreConsentBatches(file: string, personUid: string, batches: Row[][], opts?: {
32
+ maxBytes?: number;
33
+ now?: () => string;
34
+ lock?: PreConsentLockOptions;
35
+ }): Promise<AppendResult>;
36
+ export interface FlushResult {
37
+ batchesSent: number;
38
+ eventsSent: number;
39
+ /** A POST failed; that batch and the ones after it are still stored. */
40
+ failed: boolean;
41
+ }
42
+ /**
43
+ * POST each stored batch in order, removing it from the file only after its
44
+ * own POST succeeds. The lock is held across the POSTs, so an append that
45
+ * arrives mid-flush waits and then adds to what is left.
46
+ */
47
+ export declare function flushPreConsentBuffer<T extends Row = Row>(file: string, personUid: string, post: (events: T[]) => Promise<unknown>, opts?: {
48
+ lock?: PreConsentLockOptions;
49
+ }): Promise<FlushResult>;
50
+ /** Delete the account's buffer without sending it. Returns true when a file was removed. */
51
+ export declare function discardPreConsentBuffer(file: string, opts?: {
52
+ lock?: PreConsentLockOptions;
53
+ }): Promise<boolean>;
54
+ export {};
55
+ //# sourceMappingURL=pre-consent-buffer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pre-consent-buffer.d.ts","sourceRoot":"","sources":["../../src/telemetry/pre-consent-buffer.ts"],"names":[],"mappings":"AA8BA,eAAO,MAAM,4BAA4B,QAAkB,CAAC;AAC5D,4EAA4E;AAC5E,eAAO,MAAM,yBAAyB,QAAgB,CAAC;AACvD,4EAA4E;AAC5E,eAAO,MAAM,wBAAwB,QAAY,CAAC;AAGlD,MAAM,WAAW,qBAAqB;IACpC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,+EAA+E;AAC/E,qBAAa,0BAA2B,SAAQ,KAAK;gBACvC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;CAI7C;AAED,MAAM,MAAM,gBAAgB,GAAG,OAAO,GAAG,OAAO,CAAC;AAEjD,KAAK,GAAG,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAanC,gGAAgG;AAChG,wBAAgB,oBAAoB,CAClC,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,MAAM,GAAE,gBAA0B,GACjC,MAAM,GAAG,SAAS,CAKpB;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,GAAG,EAAE,EAAE,CAmBnF;AAuJD,MAAM,WAAW,YAAY;IAC3B,eAAe,EAAE,MAAM,CAAC;IACxB,yGAAyG;IACzG,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,2FAA2F;AAC3F,wBAAsB,uBAAuB,CAC3C,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,GAAG,EAAE,EAAE,EAChB,IAAI,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,qBAAqB,CAAA;CAAO,GACjF,OAAO,CAAC,YAAY,CAAC,CAuBvB;AAED,MAAM,WAAW,WAAW;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,wEAAwE;IACxE,MAAM,EAAE,OAAO,CAAC;CACjB;AAED;;;;GAIG;AACH,wBAAsB,qBAAqB,CAAC,CAAC,SAAS,GAAG,GAAG,GAAG,EAC7D,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,CAAC,MAAM,EAAE,CAAC,EAAE,KAAK,OAAO,CAAC,OAAO,CAAC,EACvC,IAAI,GAAE;IAAE,IAAI,CAAC,EAAE,qBAAqB,CAAA;CAAO,GAC1C,OAAO,CAAC,WAAW,CAAC,CAgCtB;AAED,4FAA4F;AAC5F,wBAAsB,uBAAuB,CAC3C,IAAI,EAAE,MAAM,EACZ,IAAI,GAAE;IAAE,IAAI,CAAC,EAAE,qBAAqB,CAAA;CAAO,GAC1C,OAAO,CAAC,OAAO,CAAC,CAWlB"}
@@ -0,0 +1,303 @@
1
+ /**
2
+ * Pre-consent telemetry buffer (policy indigo-pre-consent-telemetry-buffer-delivery).
3
+ *
4
+ * While the server answers `consentSource: "notice-pending"`, personal rows are not
5
+ * sent. The collector stores the batches it would have sent here instead, one
6
+ * file per account, and delivers them once the same account's collection is on.
7
+ *
8
+ * Layout:
9
+ * <bufferDir>/<personUid>.jsonl usage rows (POST /v1/usage)
10
+ * <bufferDir>/skill/<personUid>.jsonl skill rows (POST /v1/skill-invocations)
11
+ * `<bufferDir>` defaults to `~/.hq/telemetry-buffer`. Each line is one stored
12
+ * batch: `{"v":1,"personUid":"prs_…","bufferedAt":"<ISO>","events":[…]}`.
13
+ *
14
+ * Rules:
15
+ * - Account-bound: the path is derived from the signed-in `personUid`; a line
16
+ * naming a different uid is never sent and is dropped on the next rewrite.
17
+ * - Delete-after-ack: flushing POSTs one stored batch at a time and removes a
18
+ * batch only after its own POST succeeds. The first failure stops the flush
19
+ * and keeps that batch and everything after it.
20
+ * - Discard on refusal: `discardPreConsentBuffer` deletes the file unsent.
21
+ * - Size cap: appends drop the oldest batches until the file fits the cap.
22
+ * - Locking: append, flush and discard each hold `<file>.lock` (created with
23
+ * O_EXCL) for their whole read-modify-write, so two collectors or two runs
24
+ * cannot overwrite each other's changes. A lock older than the stale age is
25
+ * taken to be left by a crashed process and is reclaimed.
26
+ */
27
+ import { randomBytes } from "node:crypto";
28
+ import { promises as fs } from "node:fs";
29
+ import * as path from "node:path";
30
+ export const PRE_CONSENT_BUFFER_MAX_BYTES = 5 * 1024 * 1024;
31
+ /** A lock untouched for this long is treated as abandoned and reclaimed. */
32
+ export const PRE_CONSENT_LOCK_STALE_MS = 5 * 60 * 1000;
33
+ /** How long append / flush / discard wait for the lock before giving up. */
34
+ export const PRE_CONSENT_LOCK_WAIT_MS = 10 * 1000;
35
+ const LOCK_RETRY_MS = 50;
36
+ /** The buffer lock was still held by another process when the wait ran out. */
37
+ export class PreConsentLockTimeoutError extends Error {
38
+ constructor(lockPath, waitMs) {
39
+ super(`pre-consent buffer lock ${lockPath} still held after ${waitMs} ms`);
40
+ this.name = "PreConsentLockTimeoutError";
41
+ }
42
+ }
43
+ // Person uids are server-issued `prs_*` ids. Anything with a path separator or
44
+ // a dot segment is refused so a uid can never point outside the buffer dir.
45
+ const SAFE_UID = /^[A-Za-z0-9_-]{1,128}$/;
46
+ /** Buffer file for this account and stream, or `undefined` when the uid is absent or unsafe. */
47
+ export function preConsentBufferPath(bufferDir, personUid, stream = "usage") {
48
+ if (personUid === undefined || !SAFE_UID.test(personUid))
49
+ return undefined;
50
+ return stream === "usage"
51
+ ? path.join(bufferDir, `${personUid}.jsonl`)
52
+ : path.join(bufferDir, stream, `${personUid}.jsonl`);
53
+ }
54
+ /**
55
+ * Split rows into batches the server accepts: at most `maxEvents` rows and a
56
+ * serialized `events` array of at most `maxBytes`. Rows are already bounded
57
+ * to fit a batch by the collectors.
58
+ */
59
+ export function chunkRows(rows, maxEvents, maxBytes) {
60
+ const batches = [];
61
+ let current = [];
62
+ let bytes = 2;
63
+ for (const row of rows) {
64
+ const rowBytes = Buffer.byteLength(JSON.stringify(row), "utf-8");
65
+ if (current.length > 0 &&
66
+ (current.length >= maxEvents || bytes + 1 + rowBytes > maxBytes)) {
67
+ batches.push(current);
68
+ current = [];
69
+ bytes = 2;
70
+ }
71
+ bytes += rowBytes + (current.length > 0 ? 1 : 0);
72
+ current.push(row);
73
+ }
74
+ if (current.length > 0)
75
+ batches.push(current);
76
+ return batches;
77
+ }
78
+ async function readLines(file) {
79
+ let raw;
80
+ try {
81
+ raw = await fs.readFile(file, "utf-8");
82
+ }
83
+ catch (err) {
84
+ if (err.code === "ENOENT")
85
+ return [];
86
+ throw err;
87
+ }
88
+ return raw.split("\n").filter((line) => line.trim().length > 0);
89
+ }
90
+ function parseLine(line, personUid) {
91
+ try {
92
+ const parsed = JSON.parse(line);
93
+ if (parsed.v === 1 &&
94
+ parsed.personUid === personUid &&
95
+ Array.isArray(parsed.events) &&
96
+ parsed.events.length > 0) {
97
+ return parsed;
98
+ }
99
+ }
100
+ catch {
101
+ // A torn or corrupt line is dropped.
102
+ }
103
+ return undefined;
104
+ }
105
+ function uniqueSuffix() {
106
+ return `${process.pid}.${randomBytes(6).toString("hex")}`;
107
+ }
108
+ /** Create the buffer directory as 0700. `mkdir`'s mode is filtered by the umask and ignored for an existing dir, so chmod after. */
109
+ async function ensureDir(dir) {
110
+ await fs.mkdir(dir, { recursive: true, mode: 0o700 });
111
+ await fs.chmod(dir, 0o700);
112
+ }
113
+ async function dirExists(dir) {
114
+ try {
115
+ return (await fs.stat(dir)).isDirectory();
116
+ }
117
+ catch (err) {
118
+ if (err.code === "ENOENT")
119
+ return false;
120
+ throw err;
121
+ }
122
+ }
123
+ /** Atomic rewrite (tmp + rename); an empty list removes the file. Call with the lock held. */
124
+ async function writeLines(file, lines) {
125
+ if (lines.length === 0) {
126
+ await fs.rm(file, { force: true });
127
+ return;
128
+ }
129
+ const tmp = `${file}.${uniqueSuffix()}.tmp`;
130
+ try {
131
+ await fs.writeFile(tmp, lines.map((line) => `${line}\n`).join(""), {
132
+ encoding: "utf-8",
133
+ mode: 0o600,
134
+ });
135
+ await fs.rename(tmp, file);
136
+ }
137
+ catch (err) {
138
+ await fs.rm(tmp, { force: true });
139
+ throw err;
140
+ }
141
+ }
142
+ /** The lock was already reclaimed or removed: nothing to do. Any other error is real. */
143
+ function ignoreMissing(err) {
144
+ if (err.code === "ENOENT")
145
+ return undefined;
146
+ throw err;
147
+ }
148
+ /** Remove the lock at `lockPath` if it is older than `staleMs`. Returns true when the caller should retry at once. */
149
+ async function reclaimIfStale(lockPath, staleMs) {
150
+ let mtimeMs;
151
+ try {
152
+ mtimeMs = (await fs.stat(lockPath)).mtimeMs;
153
+ }
154
+ catch (err) {
155
+ if (err.code === "ENOENT")
156
+ return true;
157
+ throw err;
158
+ }
159
+ if (Date.now() - mtimeMs <= staleMs)
160
+ return false;
161
+ // Rename first so only one waiter removes this particular stale lock.
162
+ const aside = `${lockPath}.${uniqueSuffix()}.stale`;
163
+ try {
164
+ await fs.rename(lockPath, aside);
165
+ }
166
+ catch (err) {
167
+ if (err.code === "ENOENT")
168
+ return true;
169
+ throw err;
170
+ }
171
+ await fs.rm(aside, { force: true });
172
+ return true;
173
+ }
174
+ async function acquireLock(file, opts) {
175
+ const lockPath = `${file}.lock`;
176
+ const staleMs = opts.staleMs ?? PRE_CONSENT_LOCK_STALE_MS;
177
+ const waitMs = opts.waitMs ?? PRE_CONSENT_LOCK_WAIT_MS;
178
+ const retryMs = opts.retryMs ?? LOCK_RETRY_MS;
179
+ const token = `${uniqueSuffix()} ${new Date().toISOString()}\n`;
180
+ const deadline = Date.now() + waitMs;
181
+ for (;;) {
182
+ try {
183
+ const handle = await fs.open(lockPath, "wx", 0o600);
184
+ try {
185
+ await handle.writeFile(token, "utf-8");
186
+ }
187
+ finally {
188
+ await handle.close();
189
+ }
190
+ return {
191
+ async refresh() {
192
+ const now = new Date();
193
+ await fs.utimes(lockPath, now, now).catch(ignoreMissing);
194
+ },
195
+ async release() {
196
+ // Remove the lock only if it is still ours: after a stale reclaim it
197
+ // may belong to another process.
198
+ const current = await fs.readFile(lockPath, "utf-8").catch(ignoreMissing);
199
+ if (current === token)
200
+ await fs.rm(lockPath, { force: true });
201
+ },
202
+ };
203
+ }
204
+ catch (err) {
205
+ if (err.code !== "EEXIST")
206
+ throw err;
207
+ }
208
+ if (await reclaimIfStale(lockPath, staleMs))
209
+ continue;
210
+ if (Date.now() >= deadline)
211
+ throw new PreConsentLockTimeoutError(lockPath, waitMs);
212
+ await new Promise((resolve) => setTimeout(resolve, retryMs));
213
+ }
214
+ }
215
+ async function withLock(file, opts, fn) {
216
+ const lock = await acquireLock(file, opts);
217
+ try {
218
+ return await fn(lock);
219
+ }
220
+ finally {
221
+ await lock.release();
222
+ }
223
+ }
224
+ /** Append batches for `personUid`, then drop the oldest until the file fits `maxBytes`. */
225
+ export async function appendPreConsentBatches(file, personUid, batches, opts = {}) {
226
+ const nonEmpty = batches.filter((batch) => batch.length > 0);
227
+ if (nonEmpty.length === 0)
228
+ return { batchesAppended: 0, batchesDropped: 0 };
229
+ const maxBytes = opts.maxBytes ?? PRE_CONSENT_BUFFER_MAX_BYTES;
230
+ const bufferedAt = (opts.now ?? (() => new Date().toISOString()))();
231
+ const added = nonEmpty.map((events) => JSON.stringify({ v: 1, personUid, bufferedAt, events }));
232
+ await ensureDir(path.dirname(file));
233
+ return withLock(file, opts.lock ?? {}, async () => {
234
+ const existing = (await readLines(file)).filter((line) => parseLine(line, personUid));
235
+ const lines = [...existing, ...added];
236
+ let total = lines.reduce((sum, line) => sum + Buffer.byteLength(line, "utf-8") + 1, 0);
237
+ let dropped = 0;
238
+ while (lines.length > 0 && total > maxBytes) {
239
+ const oldest = lines.shift();
240
+ total -= Buffer.byteLength(oldest, "utf-8") + 1;
241
+ dropped++;
242
+ }
243
+ await writeLines(file, lines);
244
+ return { batchesAppended: added.length, batchesDropped: dropped };
245
+ });
246
+ }
247
+ /**
248
+ * POST each stored batch in order, removing it from the file only after its
249
+ * own POST succeeds. The lock is held across the POSTs, so an append that
250
+ * arrives mid-flush waits and then adds to what is left.
251
+ */
252
+ export async function flushPreConsentBuffer(file, personUid, post, opts = {}) {
253
+ // Nothing was ever buffered here; do not create the directory just to look.
254
+ if (!(await dirExists(path.dirname(file))))
255
+ return { batchesSent: 0, eventsSent: 0, failed: false };
256
+ return withLock(file, opts.lock ?? {}, async (lock) => {
257
+ const lines = await readLines(file);
258
+ if (lines.length === 0)
259
+ return { batchesSent: 0, eventsSent: 0, failed: false };
260
+ const remaining = lines
261
+ .map((line) => ({ line, batch: parseLine(line, personUid) }))
262
+ .filter((entry) => entry.batch !== undefined);
263
+ if (remaining.length !== lines.length) {
264
+ await writeLines(file, remaining.map((entry) => entry.line));
265
+ }
266
+ let batchesSent = 0;
267
+ let eventsSent = 0;
268
+ while (remaining.length > 0) {
269
+ const { batch } = remaining[0];
270
+ try {
271
+ // Stored rows were written by this module from the collector's own
272
+ // wire rows, so they have the collector's row type.
273
+ await post(batch.events);
274
+ }
275
+ catch {
276
+ return { batchesSent, eventsSent, failed: true };
277
+ }
278
+ remaining.shift();
279
+ batchesSent++;
280
+ eventsSent += batch.events.length;
281
+ await writeLines(file, remaining.map((entry) => entry.line));
282
+ await lock.refresh();
283
+ }
284
+ return { batchesSent, eventsSent, failed: false };
285
+ });
286
+ }
287
+ /** Delete the account's buffer without sending it. Returns true when a file was removed. */
288
+ export async function discardPreConsentBuffer(file, opts = {}) {
289
+ if (!(await dirExists(path.dirname(file))))
290
+ return false;
291
+ return withLock(file, opts.lock ?? {}, async () => {
292
+ try {
293
+ await fs.unlink(file);
294
+ return true;
295
+ }
296
+ catch (err) {
297
+ if (err.code === "ENOENT")
298
+ return false;
299
+ throw err;
300
+ }
301
+ });
302
+ }
303
+ //# sourceMappingURL=pre-consent-buffer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pre-consent-buffer.js","sourceRoot":"","sources":["../../src/telemetry/pre-consent-buffer.ts"],"names":[],"mappings":"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"}
@@ -23,6 +23,7 @@
23
23
  * sync. The cursor is only advanced for batches the server 2xx'd, so a
24
24
  * transient outage retries automatically on the next sync.
25
25
  */
26
+ import { type DecidedOptInSource } from "./telemetry/consent-source.js";
26
27
  import type { TelemetryOptInResponse, UsageBatch, UsageIngestResult } from "./vault-client.js";
27
28
  /**
28
29
  * Minimal subset of `VaultClient` the collector needs. Declared as an
@@ -116,6 +117,8 @@ export interface CollectTelemetryOptions {
116
117
  * check is skipped (including menubar fallback).
117
118
  */
118
119
  forceCollect?: boolean;
120
+ /** Override `~/.hq/telemetry-buffer` (the pre-consent buffer) for tests. */
121
+ bufferDir?: string;
119
122
  /** Diagnostic sink. No-op by default. */
120
123
  log?: (msg: string) => void;
121
124
  }
@@ -133,7 +136,13 @@ export interface CollectTelemetryResult {
133
136
  */
134
137
  companyScopeOnly?: boolean;
135
138
  /** Source for the `enabled` decision — useful for diagnosing missing-events reports. */
136
- optInSource: "server" | "menubar-fallback" | "menubar-reasserted" | "agent-required" | "skipped";
139
+ optInSource: DecidedOptInSource | "menubar-reasserted" | "skipped";
140
+ /** Personal rows held in the account's pre-consent buffer this run (notice-pending). */
141
+ preConsentBuffered?: number;
142
+ /** Previously buffered rows delivered this run, before the regular scan. */
143
+ preConsentFlushed?: number;
144
+ /** The account refused (personal opt-out) and its buffer was deleted unsent. */
145
+ preConsentDiscarded?: boolean;
137
146
  /** How many `.jsonl` files we considered (before the cursor diff). */
138
147
  filesScanned: number;
139
148
  /** Total events successfully POSTed across all batches. */
@@ -1 +1 @@
1
- {"version":3,"file":"telemetry.d.ts","sourceRoot":"","sources":["../src/telemetry.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"telemetry.d.ts","sourceRoot":"","sources":["../src/telemetry.ts"],"names":[],"mappings":"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"}