@indigoai-us/hq-cloud 6.11.9 → 6.11.10

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 (48) hide show
  1. package/dist/bin/sync-runner.d.ts.map +1 -1
  2. package/dist/bin/sync-runner.js +4 -0
  3. package/dist/bin/sync-runner.js.map +1 -1
  4. package/dist/bin/sync-runner.test.js +72 -0
  5. package/dist/bin/sync-runner.test.js.map +1 -1
  6. package/dist/cli/reindex.test.js +44 -0
  7. package/dist/cli/reindex.test.js.map +1 -1
  8. package/dist/cli/rescue-classify-ordering.test.js +25 -0
  9. package/dist/cli/rescue-classify-ordering.test.js.map +1 -1
  10. package/dist/company-resolver.d.ts +77 -0
  11. package/dist/company-resolver.d.ts.map +1 -0
  12. package/dist/company-resolver.js +124 -0
  13. package/dist/company-resolver.js.map +1 -0
  14. package/dist/company-resolver.test.d.ts +7 -0
  15. package/dist/company-resolver.test.d.ts.map +1 -0
  16. package/dist/company-resolver.test.js +120 -0
  17. package/dist/company-resolver.test.js.map +1 -0
  18. package/dist/skill-telemetry.d.ts.map +1 -1
  19. package/dist/skill-telemetry.js +22 -3
  20. package/dist/skill-telemetry.js.map +1 -1
  21. package/dist/skill-telemetry.test.js +101 -1
  22. package/dist/skill-telemetry.test.js.map +1 -1
  23. package/dist/sync/pull-scope.d.ts +1 -0
  24. package/dist/sync/pull-scope.d.ts.map +1 -1
  25. package/dist/sync/pull-scope.js +26 -0
  26. package/dist/sync/pull-scope.js.map +1 -1
  27. package/dist/telemetry.d.ts +18 -1
  28. package/dist/telemetry.d.ts.map +1 -1
  29. package/dist/telemetry.js +28 -2
  30. package/dist/telemetry.js.map +1 -1
  31. package/dist/telemetry.test.js +93 -1
  32. package/dist/telemetry.test.js.map +1 -1
  33. package/dist/vault-client.d.ts +4 -2
  34. package/dist/vault-client.d.ts.map +1 -1
  35. package/dist/vault-client.js.map +1 -1
  36. package/package.json +1 -1
  37. package/src/bin/sync-runner.test.ts +90 -0
  38. package/src/bin/sync-runner.ts +4 -0
  39. package/src/cli/reindex.test.ts +53 -0
  40. package/src/cli/rescue-classify-ordering.test.ts +28 -0
  41. package/src/company-resolver.test.ts +136 -0
  42. package/src/company-resolver.ts +147 -0
  43. package/src/skill-telemetry.test.ts +126 -1
  44. package/src/skill-telemetry.ts +26 -3
  45. package/src/sync/pull-scope.ts +26 -1
  46. package/src/telemetry.test.ts +118 -1
  47. package/src/telemetry.ts +50 -2
  48. package/src/vault-client.ts +4 -2
package/src/telemetry.ts CHANGED
@@ -27,6 +27,11 @@ import { promises as fs } from "node:fs";
27
27
  import * as os from "node:os";
28
28
  import * as path from "node:path";
29
29
 
30
+ import {
31
+ buildRepoCompanyMap,
32
+ resolveCompanyForCwd,
33
+ type RepoCompanyMap,
34
+ } from "./company-resolver.js";
30
35
  import type {
31
36
  TelemetryOptInResponse,
32
37
  UsageBatch,
@@ -51,6 +56,15 @@ export interface CollectTelemetryOptions {
51
56
  machineId: string;
52
57
  /** Version of the wrapping caller (menubar app, CLI, etc.). Reaches CloudWatch metrics as the `installerVersion` dimension. */
53
58
  installerVersion: string;
59
+ /**
60
+ * HQ root, used to resolve each event's `cwd` → owning repo → owning company
61
+ * via `<hqRoot>/companies/manifest.yaml` and stamp `companyUid` on the event
62
+ * (surface-hq-console-telemetry US-002). The manifest is parsed ONCE per run
63
+ * (see `buildRepoCompanyMap`); the resulting map is reused for every event.
64
+ * When omitted (or when no repo matches), `companyUid` is left UNSET and the
65
+ * server treats the event as unattributed/personal.
66
+ */
67
+ hqRoot?: string;
54
68
  /** Override `~/.claude/projects` for tests. */
55
69
  claudeProjectsRoot?: string;
56
70
  /** Override `~/.hq/telemetry-cursor.json` for tests. */
@@ -148,12 +162,23 @@ const KEEP_TOP_LEVEL = [
148
162
  * camelCase top-level fields. The original `message` object — which
149
163
  * carries prompt/response text, thinking, and tool data — is dropped.
150
164
  *
165
+ * `companyUid` (US-002): when the caller has resolved the row's `cwd` to an
166
+ * owning company (`resolveCompanyForCwd`), it passes that `cmp_*` uid here and
167
+ * it is stamped on the wire row. It is on the server's KEEP allowlist
168
+ * (`apps/hq-pro/src/vault-service/handlers/usage.ts`). When `companyUid` is
169
+ * undefined (cwd maps to no company repo) the field is OMITTED — the server
170
+ * treats absence as unattributed/personal. The reserved value `unattributed`
171
+ * is never produced (it can only come from a resolved manifest `cmp_*` uid).
172
+ *
151
173
  * Returns `null` when the input isn't an object. Empty results (e.g. a row
152
174
  * with no recognised fields) are still returned as `{}` and emitted; the
153
175
  * server accepts empty rows and they're useful as a "Claude Code was run at
154
176
  * this time" heartbeat.
155
177
  */
156
- export function sanitizeRow(row: unknown): Record<string, unknown> | null {
178
+ export function sanitizeRow(
179
+ row: unknown,
180
+ companyUid?: string,
181
+ ): Record<string, unknown> | null {
157
182
  if (!row || typeof row !== "object" || Array.isArray(row)) return null;
158
183
  const obj = row as Record<string, unknown>;
159
184
  const out: Record<string, unknown> = {};
@@ -164,6 +189,12 @@ export function sanitizeRow(row: unknown): Record<string, unknown> | null {
164
189
  }
165
190
  }
166
191
 
192
+ // Stamp the resolved company attribution. Omit when unresolved so the server
193
+ // reads it as unattributed/personal. Never the reserved `unattributed`.
194
+ if (companyUid !== undefined) {
195
+ out.companyUid = companyUid;
196
+ }
197
+
167
198
  const message = obj.message;
168
199
  if (message && typeof message === "object" && !Array.isArray(message)) {
169
200
  const m = message as Record<string, unknown>;
@@ -261,6 +292,13 @@ export async function collectAndSendTelemetry(
261
292
  const menubarPath = opts.menubarPath ?? path.join(home, ".hq", "menubar.json");
262
293
  const log = opts.log ?? (() => {});
263
294
 
295
+ // Company attribution (US-002): parse the manifest ONCE per run and reuse the
296
+ // repo-path→companyUid map for every event below. No per-event manifest read.
297
+ // When `hqRoot` is omitted the map is empty → every event stays unattributed.
298
+ const repoCompanyMap: RepoCompanyMap = opts.hqRoot
299
+ ? await buildRepoCompanyMap(opts.hqRoot)
300
+ : { entries: [] };
301
+
264
302
  // 1. Opt-in check (server-authoritative, with local fallback).
265
303
  let enabled: boolean;
266
304
  let optInSource: CollectTelemetryResult["optInSource"];
@@ -390,7 +428,17 @@ export async function collectAndSendTelemetry(
390
428
  } catch {
391
429
  continue;
392
430
  }
393
- const sanitized = sanitizeRow(parsed);
431
+ // Resolve this row's cwd → owning company (cmp_* uid) before sanitizing,
432
+ // using the per-run map. Unresolved → undefined → companyUid omitted.
433
+ const rowCwd =
434
+ parsed && typeof parsed === "object" && !Array.isArray(parsed)
435
+ ? (parsed as Record<string, unknown>).cwd
436
+ : undefined;
437
+ const companyUid = resolveCompanyForCwd(
438
+ typeof rowCwd === "string" ? rowCwd : undefined,
439
+ repoCompanyMap,
440
+ );
441
+ const sanitized = sanitizeRow(parsed, companyUid);
394
442
  if (!sanitized) continue;
395
443
 
396
444
  // Cost of appending this row to the current batch: the row's JSON
@@ -393,7 +393,8 @@ export interface UsageBatch {
393
393
  * Sanitized event rows. Each row is a plain object containing only the
394
394
  * fields in the server's KEEP allowlist (sessionId, timestamp, uuid, cwd,
395
395
  * gitBranch, userType, model, inputTokens, outputTokens,
396
- * cacheCreationInputTokens, cacheReadInputTokens). Any extra field is
396
+ * cacheCreationInputTokens, cacheReadInputTokens, and the optional
397
+ * companyUid edge-attribution field — US-002). Any extra field is
397
398
  * rejected by hq-pro with `unexpected-event-field`, so the sanitizer in
398
399
  * `./telemetry.ts` is the only thing allowed to produce these.
399
400
  */
@@ -416,7 +417,8 @@ export interface SkillInvocationBatch {
416
417
  /**
417
418
  * Skill-invocation event rows. Each row contains only the fields in the
418
419
  * server's KEEP allowlist (skill, source, sessionId, timestamp, uuid, cwd,
419
- * hasArgs). Raw argument text is never includedsee the privacy note in
420
+ * hasArgs, and the optional companyUid edge-attribution fieldUS-002).
421
+ * Raw argument text is never included — see the privacy note in
420
422
  * `./skill-telemetry.ts`. Any extra field is rejected by hq-pro with
421
423
  * `unexpected-event-field`, so the extractor in `./skill-telemetry.ts` is the
422
424
  * only thing allowed to produce these.