@indigoai-us/hq-cloud 6.15.41 → 6.15.43

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 (50) hide show
  1. package/dist/bin/sync-runner-company.d.ts.map +1 -1
  2. package/dist/bin/sync-runner-company.js +48 -3
  3. package/dist/bin/sync-runner-company.js.map +1 -1
  4. package/dist/bin/sync-runner-company.test.js +135 -0
  5. package/dist/bin/sync-runner-company.test.js.map +1 -1
  6. package/dist/bin/sync-runner-watch-loop.d.ts.map +1 -1
  7. package/dist/bin/sync-runner-watch-loop.js +22 -6
  8. package/dist/bin/sync-runner-watch-loop.js.map +1 -1
  9. package/dist/bin/sync-runner.d.ts +2 -0
  10. package/dist/bin/sync-runner.d.ts.map +1 -1
  11. package/dist/bin/sync-runner.js.map +1 -1
  12. package/dist/bin/sync-runner.test.js +93 -16
  13. package/dist/bin/sync-runner.test.js.map +1 -1
  14. package/dist/cli/share.d.ts +5 -17
  15. package/dist/cli/share.d.ts.map +1 -1
  16. package/dist/cli/share.js +16 -62
  17. package/dist/cli/share.js.map +1 -1
  18. package/dist/cli/share.test.js +57 -89
  19. package/dist/cli/share.test.js.map +1 -1
  20. package/dist/cli/sync.d.ts +16 -5
  21. package/dist/cli/sync.d.ts.map +1 -1
  22. package/dist/cli/sync.js +16 -63
  23. package/dist/cli/sync.js.map +1 -1
  24. package/dist/cli/sync.test.js +28 -54
  25. package/dist/cli/sync.test.js.map +1 -1
  26. package/dist/local-path-codec.test.js +20 -0
  27. package/dist/local-path-codec.test.js.map +1 -1
  28. package/dist/s3.d.ts +1 -1
  29. package/dist/s3.d.ts.map +1 -1
  30. package/dist/s3.js +7 -23
  31. package/dist/s3.js.map +1 -1
  32. package/dist/s3.test.js +13 -20
  33. package/dist/s3.test.js.map +1 -1
  34. package/dist/sync-core.d.ts +16 -0
  35. package/dist/sync-core.d.ts.map +1 -1
  36. package/dist/sync-core.js +38 -0
  37. package/dist/sync-core.js.map +1 -1
  38. package/dist/sync-progress.d.ts +18 -1
  39. package/dist/sync-progress.d.ts.map +1 -1
  40. package/dist/sync-progress.js +113 -7
  41. package/dist/sync-progress.js.map +1 -1
  42. package/dist/sync-progress.test.js +32 -2
  43. package/dist/sync-progress.test.js.map +1 -1
  44. package/dist/watcher.d.ts +26 -5
  45. package/dist/watcher.d.ts.map +1 -1
  46. package/dist/watcher.js +93 -13
  47. package/dist/watcher.js.map +1 -1
  48. package/dist/watcher.test.js +45 -1
  49. package/dist/watcher.test.js.map +1 -1
  50. package/package.json +1 -1
@@ -3249,18 +3249,11 @@ describe("sync", () => {
3249
3249
  expect(result.filesDownloaded).toBe(1);
3250
3250
  expect(result.filesExcludedByPolicy).toBeGreaterThanOrEqual(1);
3251
3251
  });
3252
- it("skips remote keys under companies/<slug>/ in a company vault (doubly-scoped corrupt object — frogbear exit-2 regression)", async () => {
3253
- // A company vault is already anchored at its company root, so its keys are
3254
- // bucket-relative. A remote key literally beginning with `companies/...` is
3255
- // a doubly-scoped corrupt object: the vault-service refuses to presign it
3256
- // (INVALID_KEY_COMPANIES_SCOPED), so the per-file GET fails and the whole
3257
- // company wedges at "errored" (runner exit 2) on every run. Verified live
3258
- // 2026-06-16 against frogbear. The pull planner must refuse it at planning
3259
- // time — same policy bucket as the malformed-key / ephemeral filters.
3260
- const corruptKey = "companies/acme/drafts/reports/signals-2026-06-15.html";
3252
+ it("round-trips a companies/ key into the identical nested company-vault path", async () => {
3253
+ const nestedKey = "companies/acme/audit/chat-skills/x.json";
3261
3254
  vi.mocked(s3Module.listRemoteFiles).mockResolvedValueOnce([
3262
- // Doubly-scoped corrupt key — must be filtered, never downloaded.
3263
- { key: corruptKey, size: 2152, lastModified: new Date(), etag: '"corrupt"' },
3255
+ // `companies/` is an ordinary directory beneath this company's root.
3256
+ { key: nestedKey, size: 2152, lastModified: new Date(), etag: '"nested"' },
3264
3257
  // A legitimate bucket-relative key — must still download.
3265
3258
  { key: "docs/handoff.md", size: 42, lastModified: new Date(), etag: '"ok"' },
3266
3259
  ]);
@@ -3270,17 +3263,12 @@ describe("sync", () => {
3270
3263
  hqRoot: tmpDir,
3271
3264
  });
3272
3265
  const companyRoot = path.join(tmpDir, "companies", "acme");
3273
- // The corrupt key MUST NOT be materialized at its doubled local path.
3274
- expect(fs.existsSync(path.join(companyRoot, "companies", "acme", "drafts", "reports", "signals-2026-06-15.html"))).toBe(false);
3275
- // The vault-service GET must never be attempted for the corrupt key — the
3276
- // failing presign is the exact symptom this guard removes.
3277
- for (const call of vi.mocked(s3Module.downloadFile).mock.calls) {
3278
- expect(call[1]).not.toBe(corruptKey);
3279
- }
3266
+ // The remote key materializes exactly beneath the selected company root.
3267
+ expect(fs.existsSync(path.join(companyRoot, "companies", "acme", "audit", "chat-skills", "x.json"))).toBe(true);
3268
+ expect(vi.mocked(s3Module.downloadFile).mock.calls.map((call) => call[1])).toContain(nestedKey);
3280
3269
  // The legitimate bucket-relative key MUST still download.
3281
3270
  expect(fs.existsSync(path.join(companyRoot, "docs", "handoff.md"))).toBe(true);
3282
- expect(result.filesDownloaded).toBe(1);
3283
- expect(result.filesExcludedByPolicy).toBeGreaterThanOrEqual(1);
3271
+ expect(result.filesDownloaded).toBe(2);
3284
3272
  expect(result.aborted).toBe(false);
3285
3273
  });
3286
3274
  it("F02: rejects traversal remote keys before they can escape the company root", async () => {
@@ -4753,7 +4741,7 @@ describe("reportNewFilesToNotify chunking (server cap = 1000 files/report)", ()
4753
4741
  expect(fetchMock).not.toHaveBeenCalled();
4754
4742
  });
4755
4743
  });
4756
- describe("scope-invalid key hardening (companies/ prefix in a company vault — incident 2026-07-11)", () => {
4744
+ describe("nested companies directories in a company vault", () => {
4757
4745
  let tmpDir;
4758
4746
  let stateDir;
4759
4747
  let journalPath;
@@ -4781,15 +4769,15 @@ describe("scope-invalid key hardening (companies/ prefix in a company vault —
4781
4769
  fs.rmSync(stateDir, { recursive: true, force: true });
4782
4770
  delete process.env.HQ_STATE_DIR;
4783
4771
  });
4784
- it("skips a companies/-prefixed remote key instead of erroring the whole company sync", async () => {
4785
- // The incident shape: 25 poisoned `companies/indigo/…` objects in the
4786
- // company bucket; the presign download API rejects them
4787
- // (INVALID_KEY_COMPANIES_SCOPED), so attempting to download errored the
4788
- // whole company sync and gated heartbeat toolset refreshes fleet-wide.
4789
- // One bad object must never take down a company sync.
4772
+ it("downloads the live 25-file nested companies/ shape without a skip event", async () => {
4790
4773
  vi.mocked(s3Module.listRemoteFiles).mockReset();
4791
4774
  vi.mocked(s3Module.listRemoteFiles).mockResolvedValue([
4792
- { key: "companies/acme/knowledge/poison.md", size: 10, lastModified: new Date(), etag: '"p1"' },
4775
+ ...Array.from({ length: 25 }, (_, index) => ({
4776
+ key: `companies/acme/audit/chat-skills/file-${index.toString().padStart(2, "0")}.json`,
4777
+ size: 10,
4778
+ lastModified: new Date(),
4779
+ etag: `"nested-${index}"`,
4780
+ })),
4793
4781
  { key: "docs/good.md", size: 5, lastModified: new Date(), etag: '"g1"' },
4794
4782
  ]);
4795
4783
  const events = [];
@@ -4799,26 +4787,16 @@ describe("scope-invalid key hardening (companies/ prefix in a company vault —
4799
4787
  hqRoot: tmpDir,
4800
4788
  onEvent: (e) => events.push(e),
4801
4789
  });
4802
- // The good file syncs; the poisoned key is never downloaded.
4803
4790
  expect(result.aborted).toBe(false);
4804
- expect(result.filesDownloaded).toBe(1);
4791
+ expect(result.filesDownloaded).toBe(26);
4805
4792
  expect(fs.existsSync(path.join(tmpDir, "companies", "acme", "docs", "good.md"))).toBe(true);
4806
- expect(fs.existsSync(path.join(tmpDir, "companies", "acme", "companies", "acme", "knowledge", "poison.md"))).toBe(false);
4793
+ expect(fs.existsSync(path.join(tmpDir, "companies", "acme", "companies", "acme", "audit", "chat-skills", "file-24.json"))).toBe(true);
4807
4794
  const downloadedKeys = vi
4808
4795
  .mocked(s3Module.downloadFile)
4809
4796
  .mock.calls.map((c) => c[1]);
4810
- expect(downloadedKeys).not.toContain("companies/acme/knowledge/poison.md");
4811
- // Counted as policy-excluded, surfaced as a benign per-key warning event —
4812
- // deliberately NOT type:"error" (an error event flips the runner to exit 2).
4813
- expect(result.filesExcludedByPolicy).toBeGreaterThanOrEqual(1);
4797
+ expect(downloadedKeys).toContain("companies/acme/audit/chat-skills/file-00.json");
4814
4798
  const skipEvents = events.filter((e) => e.type === "skip-invalid-scoped-key");
4815
- expect(skipEvents).toEqual([
4816
- {
4817
- type: "skip-invalid-scoped-key",
4818
- path: "companies/acme/knowledge/poison.md",
4819
- errorCode: "INVALID_KEY_COMPANIES_SCOPED",
4820
- },
4821
- ]);
4799
+ expect(skipEvents).toEqual([]);
4822
4800
  expect(events.filter((e) => e.type === "error")).toEqual([]);
4823
4801
  });
4824
4802
  it("skips a control-character remote key before presigning while transferring valid keys", async () => {
@@ -4861,13 +4839,9 @@ describe("scope-invalid key hardening (companies/ prefix in a company vault —
4861
4839
  ]);
4862
4840
  expect(events.filter((event) => event.type === "error")).toEqual([]);
4863
4841
  });
4864
- it("cleans a doubled-tree journal entry via tombstone (local delete + journal drop) without HEAD-verifying the invalid key", async () => {
4865
- // A doubled local tree (companies/acme/companies/acme/…) previously
4866
- // poisoned the bucket; the poisoned objects were deleted server-side.
4867
- // The journal entry at the invalid key must drain as a tombstone —
4868
- // local copy removed, journal entry dropped — and must NOT be
4869
- // HEAD-verified (the presign transport rejects the key, which would
4870
- // defer the tombstone forever) and must NOT be re-uploaded.
4842
+ it("tombstones a remote-missing nested companies/ journal entry after HEAD verification", async () => {
4843
+ // A nested `companies/` key follows the same protected journal-tombstone
4844
+ // path as every other vault-relative file.
4871
4845
  const companyRoot = path.join(tmpDir, "companies", "acme");
4872
4846
  const doubled = path.join(companyRoot, "companies", "acme", "knowledge");
4873
4847
  fs.mkdirSync(doubled, { recursive: true });
@@ -4890,25 +4864,25 @@ describe("scope-invalid key hardening (companies/ prefix in a company vault —
4890
4864
  },
4891
4865
  },
4892
4866
  }));
4893
- // Remote LIST no longer contains the poisoned key (manual cleanup done).
4867
+ // Remote LIST no longer contains the nested key.
4894
4868
  vi.mocked(s3Module.listRemoteFiles).mockReset();
4895
4869
  vi.mocked(s3Module.listRemoteFiles).mockResolvedValue([]);
4870
+ vi.mocked(s3Module.headRemoteFile).mockResolvedValue(null);
4896
4871
  const result = await sync({
4897
4872
  company: "acme",
4898
4873
  vaultConfig: mockConfig,
4899
4874
  hqRoot: tmpDir,
4900
4875
  });
4901
- // Cleaned, not re-uploaded: local doubled-tree copy gone, journal dropped.
4876
+ // Cleaned, not re-uploaded: local nested copy gone, journal dropped.
4902
4877
  expect(fs.existsSync(path.join(doubled, "poison.md"))).toBe(false);
4903
4878
  expect(result.filesTombstoned).toBeGreaterThanOrEqual(1);
4904
4879
  const journal = journalAt(journalPath);
4905
4880
  expect(journal.files["companies/acme/knowledge/poison.md"]).toBeUndefined();
4906
- // No HEAD verify against the invalid key — the server validator would
4907
- // reject it (INVALID_KEY_COMPANIES_SCOPED) and defer the cleanup forever.
4881
+ // The normal HEAD verification confirms it is safe to tombstone.
4908
4882
  const headKeys = vi
4909
4883
  .mocked(s3Module.headRemoteFile)
4910
4884
  .mock.calls.map((c) => c[1]);
4911
- expect(headKeys).not.toContain("companies/acme/knowledge/poison.md");
4885
+ expect(headKeys).toContain("companies/acme/knowledge/poison.md");
4912
4886
  // Never re-uploaded.
4913
4887
  expect(vi.mocked(s3Module.uploadFile)).not.toHaveBeenCalled();
4914
4888
  });