@indigoai-us/hq-cloud 6.15.30 → 6.15.31
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.
- package/dist/bin/sync-runner-company.d.ts.map +1 -1
- package/dist/bin/sync-runner-company.js +24 -13
- package/dist/bin/sync-runner-company.js.map +1 -1
- package/dist/bin/sync-runner-company.test.js +0 -71
- package/dist/bin/sync-runner-company.test.js.map +1 -1
- package/dist/bin/sync-runner-watch-loop.d.ts +0 -10
- package/dist/bin/sync-runner-watch-loop.d.ts.map +1 -1
- package/dist/bin/sync-runner-watch-loop.js +13 -170
- package/dist/bin/sync-runner-watch-loop.js.map +1 -1
- package/dist/bin/sync-runner-watch-routes.d.ts +1 -16
- package/dist/bin/sync-runner-watch-routes.d.ts.map +1 -1
- package/dist/bin/sync-runner-watch-routes.js +2 -40
- package/dist/bin/sync-runner-watch-routes.js.map +1 -1
- package/dist/bin/sync-runner-watch-routes.test.js +1 -53
- package/dist/bin/sync-runner-watch-routes.test.js.map +1 -1
- package/dist/bin/sync-runner.d.ts +3 -15
- package/dist/bin/sync-runner.d.ts.map +1 -1
- package/dist/bin/sync-runner.js +13 -66
- package/dist/bin/sync-runner.js.map +1 -1
- package/dist/bin/sync-runner.test.js +13 -433
- package/dist/bin/sync-runner.test.js.map +1 -1
- package/dist/cli/conflict.d.ts +2 -25
- package/dist/cli/conflict.d.ts.map +1 -1
- package/dist/cli/conflict.js +4 -29
- package/dist/cli/conflict.js.map +1 -1
- package/dist/cli/share.d.ts +4 -40
- package/dist/cli/share.d.ts.map +1 -1
- package/dist/cli/share.js +86 -478
- package/dist/cli/share.js.map +1 -1
- package/dist/cli/share.test.js +150 -845
- package/dist/cli/share.test.js.map +1 -1
- package/dist/cli/sync-scope.test.js +18 -147
- package/dist/cli/sync-scope.test.js.map +1 -1
- package/dist/cli/sync.d.ts +0 -20
- package/dist/cli/sync.d.ts.map +1 -1
- package/dist/cli/sync.js +29 -261
- package/dist/cli/sync.js.map +1 -1
- package/dist/cli/sync.test.js +90 -519
- package/dist/cli/sync.test.js.map +1 -1
- package/dist/journal.d.ts +0 -1
- package/dist/journal.d.ts.map +1 -1
- package/dist/journal.js +25 -155
- package/dist/journal.js.map +1 -1
- package/dist/journal.test.js +11 -29
- package/dist/journal.test.js.map +1 -1
- package/dist/object-io.d.ts +13 -34
- package/dist/object-io.d.ts.map +1 -1
- package/dist/object-io.js +29 -154
- package/dist/object-io.js.map +1 -1
- package/dist/outcome-telemetry.d.ts +1 -7
- package/dist/outcome-telemetry.d.ts.map +1 -1
- package/dist/outcome-telemetry.js +9 -15
- package/dist/outcome-telemetry.js.map +1 -1
- package/dist/outcome-telemetry.test.js +0 -15
- package/dist/outcome-telemetry.test.js.map +1 -1
- package/dist/outposts/client.d.ts +2 -2
- package/dist/outposts/client.d.ts.map +1 -1
- package/dist/outposts/client.js +3 -4
- package/dist/outposts/client.js.map +1 -1
- package/dist/outposts/command.d.ts.map +1 -1
- package/dist/outposts/command.js +6 -9
- package/dist/outposts/command.js.map +1 -1
- package/dist/outposts/command.test.js +2 -59
- package/dist/outposts/command.test.js.map +1 -1
- package/dist/outposts/remote-command.d.ts +8 -8
- package/dist/outposts/remote-command.d.ts.map +1 -1
- package/dist/outposts/remote-command.js +8 -10
- package/dist/outposts/remote-command.js.map +1 -1
- package/dist/prefix-coalesce.d.ts +0 -14
- package/dist/prefix-coalesce.d.ts.map +1 -1
- package/dist/prefix-coalesce.js +0 -29
- package/dist/prefix-coalesce.js.map +1 -1
- package/dist/prefix-coalesce.test.js +1 -22
- package/dist/prefix-coalesce.test.js.map +1 -1
- package/dist/skill-telemetry.d.ts +1 -6
- package/dist/skill-telemetry.d.ts.map +1 -1
- package/dist/skill-telemetry.js +8 -14
- package/dist/skill-telemetry.js.map +1 -1
- package/dist/skill-telemetry.test.js +0 -43
- package/dist/skill-telemetry.test.js.map +1 -1
- package/dist/sts-credential-refresh.test.js +2 -87
- package/dist/sts-credential-refresh.test.js.map +1 -1
- package/dist/sync/state-store.d.ts.map +1 -1
- package/dist/sync/state-store.js +1 -20
- package/dist/sync/state-store.js.map +1 -1
- package/dist/sync/state-store.test.js +0 -64
- package/dist/sync/state-store.test.js.map +1 -1
- package/dist/sync-core.d.ts.map +1 -1
- package/dist/sync-core.js +2 -7
- package/dist/sync-core.js.map +1 -1
- package/dist/sync-core.test.js +0 -36
- package/dist/sync-core.test.js.map +1 -1
- package/dist/telemetry.d.ts +6 -30
- package/dist/telemetry.d.ts.map +1 -1
- package/dist/telemetry.js +135 -331
- package/dist/telemetry.js.map +1 -1
- package/dist/telemetry.test.js +0 -403
- package/dist/telemetry.test.js.map +1 -1
- package/dist/types.d.ts +3 -26
- package/dist/types.d.ts.map +1 -1
- package/dist/watcher.d.ts +0 -76
- package/dist/watcher.d.ts.map +1 -1
- package/dist/watcher.js +3 -105
- package/dist/watcher.js.map +1 -1
- package/dist/watcher.test.js +1 -165
- package/dist/watcher.test.js.map +1 -1
- package/package.json +1 -1
- package/dist/bandwidth.d.ts +0 -128
- package/dist/bandwidth.d.ts.map +0 -1
- package/dist/bandwidth.js +0 -254
- package/dist/bandwidth.js.map +0 -1
- package/dist/bandwidth.test.d.ts +0 -2
- package/dist/bandwidth.test.d.ts.map +0 -1
- package/dist/bandwidth.test.js +0 -243
- package/dist/bandwidth.test.js.map +0 -1
- package/dist/journal-delta-writes.test.d.ts +0 -2
- package/dist/journal-delta-writes.test.d.ts.map +0 -1
- package/dist/journal-delta-writes.test.js +0 -140
- package/dist/journal-delta-writes.test.js.map +0 -1
|
@@ -12,7 +12,6 @@ import * as os from "os";
|
|
|
12
12
|
import * as path from "path";
|
|
13
13
|
import { runRunner, runRunnerWithLoop, resolveDeletePolicy, resolveSkipPersonal, resolveSkipCompanies, selectObjectIOFactory, routeChangeToTarget, buildTargetedPushArgv, resolvePullScope, readPinnedPrefixes, SESSIONS_SCOPE_PREFIX, defaultCollectTelemetry, AUTH_REQUIRED_PASS_EXIT, } from "./sync-runner.js";
|
|
14
14
|
import { PresignObjectIO, S3SdkObjectIO } from "../object-io.js";
|
|
15
|
-
import { setBandwidthGovernorForTesting } from "../bandwidth.js";
|
|
16
15
|
import { FakeClock, } from "../watcher.js";
|
|
17
16
|
import { adaptivePollMs, advanceMonotonicDeadline, journalEntriesForWatcherDelete, shouldQuarantineWatcherDeleteIntents, } from "./sync-runner-watch-loop.js";
|
|
18
17
|
import { PERSONAL_VAULT_JOURNAL_SLUG, readJournal, writeJournal, } from "../journal.js";
|
|
@@ -216,63 +215,6 @@ describe("argv parsing", () => {
|
|
|
216
215
|
});
|
|
217
216
|
});
|
|
218
217
|
// ---------------------------------------------------------------------------
|
|
219
|
-
// bandwidth governor startup line
|
|
220
|
-
// ---------------------------------------------------------------------------
|
|
221
|
-
describe("bandwidth governor startup line", () => {
|
|
222
|
-
const ENV_KEYS = [
|
|
223
|
-
"HQ_SYNC_MAX_BYTES_PER_SEC",
|
|
224
|
-
"HQ_SYNC_MAX_CONCURRENCY",
|
|
225
|
-
"HQ_SYNC_BANDWIDTH_PERCENT",
|
|
226
|
-
];
|
|
227
|
-
let saved;
|
|
228
|
-
beforeEach(() => {
|
|
229
|
-
saved = Object.fromEntries(ENV_KEYS.map((k) => [k, process.env[k]]));
|
|
230
|
-
for (const k of ENV_KEYS)
|
|
231
|
-
delete process.env[k];
|
|
232
|
-
setBandwidthGovernorForTesting(null); // re-read env per test
|
|
233
|
-
});
|
|
234
|
-
afterEach(() => {
|
|
235
|
-
for (const k of ENV_KEYS) {
|
|
236
|
-
if (saved[k] === undefined)
|
|
237
|
-
delete process.env[k];
|
|
238
|
-
else
|
|
239
|
-
process.env[k] = saved[k];
|
|
240
|
-
}
|
|
241
|
-
setBandwidthGovernorForTesting(null);
|
|
242
|
-
});
|
|
243
|
-
it("states the effective policy on stderr when the env vars are set", async () => {
|
|
244
|
-
process.env.HQ_SYNC_MAX_BYTES_PER_SEC = "500000";
|
|
245
|
-
process.env.HQ_SYNC_MAX_CONCURRENCY = "2";
|
|
246
|
-
const deps = makeDeps({
|
|
247
|
-
createVaultClient: () => makeVaultStub({ memberships: [] }),
|
|
248
|
-
});
|
|
249
|
-
const code = await runRunner(["--companies"], deps);
|
|
250
|
-
expect(code).toBe(0);
|
|
251
|
-
expect(deps.stderr.events()).toContainEqual({
|
|
252
|
-
type: "bandwidth-policy",
|
|
253
|
-
message: "bandwidth governor: 500000 B/s cap, concurrency 2",
|
|
254
|
-
});
|
|
255
|
-
});
|
|
256
|
-
it("stays silent (exact legacy stream) when no policy is configured", async () => {
|
|
257
|
-
const deps = makeDeps({
|
|
258
|
-
createVaultClient: () => makeVaultStub({ memberships: [] }),
|
|
259
|
-
});
|
|
260
|
-
const code = await runRunner(["--companies"], deps);
|
|
261
|
-
expect(code).toBe(0);
|
|
262
|
-
expect(deps.stderr.events().some((e) => e.type === "bandwidth-policy")).toBe(false);
|
|
263
|
-
});
|
|
264
|
-
it("treats invalid env values as off (no policy line)", async () => {
|
|
265
|
-
process.env.HQ_SYNC_MAX_BYTES_PER_SEC = "0";
|
|
266
|
-
process.env.HQ_SYNC_BANDWIDTH_PERCENT = "101";
|
|
267
|
-
const deps = makeDeps({
|
|
268
|
-
createVaultClient: () => makeVaultStub({ memberships: [] }),
|
|
269
|
-
});
|
|
270
|
-
const code = await runRunner(["--companies"], deps);
|
|
271
|
-
expect(code).toBe(0);
|
|
272
|
-
expect(deps.stderr.events().some((e) => e.type === "bandwidth-policy")).toBe(false);
|
|
273
|
-
});
|
|
274
|
-
});
|
|
275
|
-
// ---------------------------------------------------------------------------
|
|
276
218
|
// auth
|
|
277
219
|
// ---------------------------------------------------------------------------
|
|
278
220
|
describe("auth", () => {
|
|
@@ -4581,18 +4523,7 @@ describe("runRunnerWithLoop — accumulate-and-drain scoped push", () => {
|
|
|
4581
4523
|
"--hq-root",
|
|
4582
4524
|
"/tmp/hq",
|
|
4583
4525
|
],
|
|
4584
|
-
|
|
4585
|
-
// the touched subtree instead of running a full --companies pull.
|
|
4586
|
-
[
|
|
4587
|
-
"--company",
|
|
4588
|
-
"indigo",
|
|
4589
|
-
"--direction",
|
|
4590
|
-
"pull",
|
|
4591
|
-
"--scope-path",
|
|
4592
|
-
"knowledge/a.md",
|
|
4593
|
-
"--hq-root",
|
|
4594
|
-
"/tmp/hq",
|
|
4595
|
-
],
|
|
4526
|
+
fullPullArgv,
|
|
4596
4527
|
]);
|
|
4597
4528
|
shutdown();
|
|
4598
4529
|
tick();
|
|
@@ -4609,7 +4540,7 @@ describe("runRunnerWithLoop — accumulate-and-drain scoped push", () => {
|
|
|
4609
4540
|
await flushLoopMicrotasks();
|
|
4610
4541
|
const pushCalls = runPass.mock.calls
|
|
4611
4542
|
.map((call) => call[0])
|
|
4612
|
-
.filter((argv) => argv.includes("--company")
|
|
4543
|
+
.filter((argv) => argv.includes("--company"));
|
|
4613
4544
|
expect(pushCalls).toEqual([
|
|
4614
4545
|
[
|
|
4615
4546
|
"--company",
|
|
@@ -4689,21 +4620,11 @@ describe("runRunnerWithLoop — accumulate-and-drain scoped push", () => {
|
|
|
4689
4620
|
await flushLoopMicrotasks();
|
|
4690
4621
|
const calls = runPass.mock.calls.map((call) => call[0]);
|
|
4691
4622
|
// No scoped push of any kind ran...
|
|
4623
|
+
expect(calls.some((argv) => argv.includes("--company"))).toBe(false);
|
|
4692
4624
|
expect(calls.some((argv) => argv.includes("--direction") &&
|
|
4693
4625
|
argv[argv.indexOf("--direction") + 1] === "push")).toBe(false);
|
|
4694
|
-
// ...only the pull leg ran
|
|
4695
|
-
expect(calls).toEqual([
|
|
4696
|
-
[
|
|
4697
|
-
"--company",
|
|
4698
|
-
"indigo",
|
|
4699
|
-
"--direction",
|
|
4700
|
-
"pull",
|
|
4701
|
-
"--scope-path",
|
|
4702
|
-
"knowledge/a.md",
|
|
4703
|
-
"--hq-root",
|
|
4704
|
-
"/tmp/hq",
|
|
4705
|
-
],
|
|
4706
|
-
]);
|
|
4626
|
+
// ...only the pull leg ran.
|
|
4627
|
+
expect(calls).toEqual([fullPullArgv]);
|
|
4707
4628
|
shutdown();
|
|
4708
4629
|
tick();
|
|
4709
4630
|
await loop;
|
|
@@ -4726,7 +4647,7 @@ describe("runRunnerWithLoop — accumulate-and-drain scoped push", () => {
|
|
|
4726
4647
|
await flushLoopMicrotasks();
|
|
4727
4648
|
const pushCalls = runPass.mock.calls
|
|
4728
4649
|
.map((call) => call[0])
|
|
4729
|
-
.filter((argv) => argv.includes("--company")
|
|
4650
|
+
.filter((argv) => argv.includes("--company"));
|
|
4730
4651
|
expect(pushCalls).toEqual([
|
|
4731
4652
|
[
|
|
4732
4653
|
"--company",
|
|
@@ -4793,17 +4714,7 @@ describe("runRunnerWithLoop — accumulate-and-drain scoped push", () => {
|
|
|
4793
4714
|
"--hq-root",
|
|
4794
4715
|
"/tmp/hq",
|
|
4795
4716
|
],
|
|
4796
|
-
|
|
4797
|
-
// subtree instead of re-walking the whole vault on every event batch.
|
|
4798
|
-
[
|
|
4799
|
-
"--personal",
|
|
4800
|
-
"--direction",
|
|
4801
|
-
"pull",
|
|
4802
|
-
"--scope-path",
|
|
4803
|
-
"knowledge/a.md",
|
|
4804
|
-
"--hq-root",
|
|
4805
|
-
"/tmp/hq",
|
|
4806
|
-
],
|
|
4717
|
+
["--personal", "--direction", "pull", "--hq-root", "/tmp/hq"],
|
|
4807
4718
|
]);
|
|
4808
4719
|
shutdown();
|
|
4809
4720
|
tick();
|
|
@@ -4839,16 +4750,7 @@ describe("runRunnerWithLoop — accumulate-and-drain scoped push", () => {
|
|
|
4839
4750
|
"--hq-root",
|
|
4840
4751
|
"/tmp/hq",
|
|
4841
4752
|
],
|
|
4842
|
-
[
|
|
4843
|
-
"--company",
|
|
4844
|
-
"acme",
|
|
4845
|
-
"--direction",
|
|
4846
|
-
"pull",
|
|
4847
|
-
"--scope-path",
|
|
4848
|
-
"knowledge/a.md",
|
|
4849
|
-
"--hq-root",
|
|
4850
|
-
"/tmp/hq",
|
|
4851
|
-
],
|
|
4753
|
+
["--company", "acme", "--direction", "pull", "--hq-root", "/tmp/hq"],
|
|
4852
4754
|
]);
|
|
4853
4755
|
shutdown();
|
|
4854
4756
|
tick();
|
|
@@ -4926,245 +4828,6 @@ describe("runRunnerWithLoop — accumulate-and-drain scoped push", () => {
|
|
|
4926
4828
|
sleep.tick();
|
|
4927
4829
|
await loop;
|
|
4928
4830
|
});
|
|
4929
|
-
it("overflow with known dropped-route hints fans out per touched route instead of a full reconcile", async () => {
|
|
4930
|
-
const watcher = makeBatchWatcherStub();
|
|
4931
|
-
const sleep = makeSteppableSleep();
|
|
4932
|
-
let triggerShutdown = () => { };
|
|
4933
|
-
const runPass = vi.fn().mockResolvedValue(completedPassOutcome());
|
|
4934
|
-
const loop = runRunnerWithLoop(watchArgv, {
|
|
4935
|
-
runPass,
|
|
4936
|
-
clock: new FakeClock(),
|
|
4937
|
-
createWatcher: () => watcher,
|
|
4938
|
-
sleep: sleep.sleep,
|
|
4939
|
-
onShutdownSignal: (handler) => {
|
|
4940
|
-
triggerShutdown = handler;
|
|
4941
|
-
return () => { };
|
|
4942
|
-
},
|
|
4943
|
-
});
|
|
4944
|
-
await flushLoopMicrotasks();
|
|
4945
|
-
runPass.mockClear();
|
|
4946
|
-
// A burst overflowed, but every dropped path recorded route evidence:
|
|
4947
|
-
// retained path in indigo, dropped paths in acme. The drain must run one
|
|
4948
|
-
// whole-route pass per touched route — never the all-companies fanout.
|
|
4949
|
-
watcher.emit("companies/indigo/a.md", {
|
|
4950
|
-
paths: new Map([
|
|
4951
|
-
["/tmp/hq/companies/indigo/a.md", "companies/indigo/a.md"],
|
|
4952
|
-
]),
|
|
4953
|
-
overflowed: true,
|
|
4954
|
-
droppedPaths: 3,
|
|
4955
|
-
droppedRouteHints: ["companies/acme/dropped.md"],
|
|
4956
|
-
});
|
|
4957
|
-
sleep.tick();
|
|
4958
|
-
// Four guarded passes run sequentially — give the loop room to finish.
|
|
4959
|
-
await flushLoopMicrotasks(40);
|
|
4960
|
-
const calls = runPass.mock.calls.map((call) => call[0]);
|
|
4961
|
-
expect(calls).toEqual([
|
|
4962
|
-
// indigo: whole-route push (no --scope-path — dropped paths unknown)…
|
|
4963
|
-
["--company", "indigo", "--direction", "push", "--hq-root", "/tmp/hq"],
|
|
4964
|
-
// …then the route's pull.
|
|
4965
|
-
["--company", "indigo", "--direction", "pull", "--hq-root", "/tmp/hq"],
|
|
4966
|
-
["--company", "acme", "--direction", "push", "--hq-root", "/tmp/hq"],
|
|
4967
|
-
["--company", "acme", "--direction", "pull", "--hq-root", "/tmp/hq"],
|
|
4968
|
-
]);
|
|
4969
|
-
expect(calls).not.toContainEqual(fullArgv);
|
|
4970
|
-
triggerShutdown();
|
|
4971
|
-
await loop;
|
|
4972
|
-
});
|
|
4973
|
-
it("falls back to a full reconcile when an event batch exceeds HQ_SYNC_EVENT_BATCH_LIMIT", async () => {
|
|
4974
|
-
const saved = process.env.HQ_SYNC_EVENT_BATCH_LIMIT;
|
|
4975
|
-
process.env.HQ_SYNC_EVENT_BATCH_LIMIT = "2";
|
|
4976
|
-
try {
|
|
4977
|
-
const watcher = makeBatchWatcherStub();
|
|
4978
|
-
const sleep = makeSteppableSleep();
|
|
4979
|
-
let triggerShutdown = () => { };
|
|
4980
|
-
const runPass = vi.fn().mockResolvedValue(completedPassOutcome());
|
|
4981
|
-
const loop = runRunnerWithLoop(watchArgv, {
|
|
4982
|
-
runPass,
|
|
4983
|
-
clock: new FakeClock(),
|
|
4984
|
-
createWatcher: () => watcher,
|
|
4985
|
-
sleep: sleep.sleep,
|
|
4986
|
-
onShutdownSignal: (handler) => {
|
|
4987
|
-
triggerShutdown = handler;
|
|
4988
|
-
return () => { };
|
|
4989
|
-
},
|
|
4990
|
-
});
|
|
4991
|
-
await flushLoopMicrotasks();
|
|
4992
|
-
runPass.mockClear();
|
|
4993
|
-
watcher.emit("companies/indigo/a.md", {
|
|
4994
|
-
paths: new Map([
|
|
4995
|
-
["/tmp/hq/companies/indigo/a.md", "companies/indigo/a.md"],
|
|
4996
|
-
["/tmp/hq/companies/indigo/b.md", "companies/indigo/b.md"],
|
|
4997
|
-
["/tmp/hq/companies/indigo/c.md", "companies/indigo/c.md"],
|
|
4998
|
-
]),
|
|
4999
|
-
});
|
|
5000
|
-
sleep.tick();
|
|
5001
|
-
await flushLoopMicrotasks();
|
|
5002
|
-
// Mass change: one full reconcile, no per-path scoped passes.
|
|
5003
|
-
expect(runPass.mock.calls.map((call) => call[0])).toEqual([fullArgv]);
|
|
5004
|
-
triggerShutdown();
|
|
5005
|
-
await loop;
|
|
5006
|
-
}
|
|
5007
|
-
finally {
|
|
5008
|
-
if (saved === undefined)
|
|
5009
|
-
delete process.env.HQ_SYNC_EVENT_BATCH_LIMIT;
|
|
5010
|
-
else
|
|
5011
|
-
process.env.HQ_SYNC_EVENT_BATCH_LIMIT = saved;
|
|
5012
|
-
}
|
|
5013
|
-
});
|
|
5014
|
-
it("a batch under the default limit stays on the targeted path", async () => {
|
|
5015
|
-
const watcher = makeBatchWatcherStub();
|
|
5016
|
-
const sleep = makeSteppableSleep();
|
|
5017
|
-
let triggerShutdown = () => { };
|
|
5018
|
-
const runPass = vi.fn().mockResolvedValue(completedPassOutcome());
|
|
5019
|
-
const loop = runRunnerWithLoop(watchArgv, {
|
|
5020
|
-
runPass,
|
|
5021
|
-
clock: new FakeClock(),
|
|
5022
|
-
createWatcher: () => watcher,
|
|
5023
|
-
sleep: sleep.sleep,
|
|
5024
|
-
onShutdownSignal: (handler) => {
|
|
5025
|
-
triggerShutdown = handler;
|
|
5026
|
-
return () => { };
|
|
5027
|
-
},
|
|
5028
|
-
});
|
|
5029
|
-
await flushLoopMicrotasks();
|
|
5030
|
-
runPass.mockClear();
|
|
5031
|
-
watcher.emit("companies/indigo/a.md", {
|
|
5032
|
-
paths: new Map([
|
|
5033
|
-
["/tmp/hq/companies/indigo/a.md", "companies/indigo/a.md"],
|
|
5034
|
-
["/tmp/hq/companies/indigo/b.md", "companies/indigo/b.md"],
|
|
5035
|
-
["/tmp/hq/companies/indigo/c.md", "companies/indigo/c.md"],
|
|
5036
|
-
]),
|
|
5037
|
-
});
|
|
5038
|
-
sleep.tick();
|
|
5039
|
-
await flushLoopMicrotasks();
|
|
5040
|
-
const calls = runPass.mock.calls.map((call) => call[0]);
|
|
5041
|
-
expect(calls).not.toContainEqual(fullArgv);
|
|
5042
|
-
expect(calls[0]).toEqual([
|
|
5043
|
-
"--company",
|
|
5044
|
-
"indigo",
|
|
5045
|
-
"--direction",
|
|
5046
|
-
"push",
|
|
5047
|
-
"--scope-path",
|
|
5048
|
-
"a.md",
|
|
5049
|
-
"--scope-path",
|
|
5050
|
-
"b.md",
|
|
5051
|
-
"--scope-path",
|
|
5052
|
-
"c.md",
|
|
5053
|
-
"--hq-root",
|
|
5054
|
-
"/tmp/hq",
|
|
5055
|
-
]);
|
|
5056
|
-
triggerShutdown();
|
|
5057
|
-
await loop;
|
|
5058
|
-
});
|
|
5059
|
-
it("a throwing targeted pass falls back to one full reconcile instead of dropping the change", async () => {
|
|
5060
|
-
const runPass = vi.fn(async (argv) => {
|
|
5061
|
-
if (argv.includes("pull") && argv.includes("--scope-path")) {
|
|
5062
|
-
throw new Error("scoped pull exploded");
|
|
5063
|
-
}
|
|
5064
|
-
return completedPassOutcome();
|
|
5065
|
-
});
|
|
5066
|
-
const { loop, watcher, tick, shutdown } = startDrainLoop(runPass);
|
|
5067
|
-
await flushLoopMicrotasks();
|
|
5068
|
-
runPass.mockClear();
|
|
5069
|
-
watcher.emit("companies/indigo/knowledge/a.md");
|
|
5070
|
-
tick();
|
|
5071
|
-
await flushLoopMicrotasks(40);
|
|
5072
|
-
expect(runPass.mock.calls.map((call) => call[0])).toEqual([
|
|
5073
|
-
[
|
|
5074
|
-
"--company",
|
|
5075
|
-
"indigo",
|
|
5076
|
-
"--direction",
|
|
5077
|
-
"push",
|
|
5078
|
-
"--scope-path",
|
|
5079
|
-
"knowledge/a.md",
|
|
5080
|
-
"--hq-root",
|
|
5081
|
-
"/tmp/hq",
|
|
5082
|
-
],
|
|
5083
|
-
// The scoped pull throws…
|
|
5084
|
-
[
|
|
5085
|
-
"--company",
|
|
5086
|
-
"indigo",
|
|
5087
|
-
"--direction",
|
|
5088
|
-
"pull",
|
|
5089
|
-
"--scope-path",
|
|
5090
|
-
"knowledge/a.md",
|
|
5091
|
-
"--hq-root",
|
|
5092
|
-
"/tmp/hq",
|
|
5093
|
-
],
|
|
5094
|
-
// …and the loop degrades to the full reconcile, never dropping the tick.
|
|
5095
|
-
fullArgv,
|
|
5096
|
-
]);
|
|
5097
|
-
shutdown();
|
|
5098
|
-
tick();
|
|
5099
|
-
await loop;
|
|
5100
|
-
});
|
|
5101
|
-
it("wires the event debounce config (default 15s quiet / 120s max-wait) into the watcher factory", async () => {
|
|
5102
|
-
const captured = [];
|
|
5103
|
-
const watcher = makeWatcherStub();
|
|
5104
|
-
const sleep = makeSteppableSleep();
|
|
5105
|
-
let triggerShutdown = () => { };
|
|
5106
|
-
const runPass = vi.fn().mockResolvedValue(completedPassOutcome());
|
|
5107
|
-
const loop = runRunnerWithLoop(watchArgv, {
|
|
5108
|
-
runPass,
|
|
5109
|
-
clock: new FakeClock(),
|
|
5110
|
-
createWatcher: (opts) => {
|
|
5111
|
-
captured.push({ debounceMs: opts.debounceMs, maxWaitMs: opts.maxWaitMs });
|
|
5112
|
-
return watcher;
|
|
5113
|
-
},
|
|
5114
|
-
sleep: sleep.sleep,
|
|
5115
|
-
onShutdownSignal: (handler) => {
|
|
5116
|
-
triggerShutdown = handler;
|
|
5117
|
-
return () => { };
|
|
5118
|
-
},
|
|
5119
|
-
});
|
|
5120
|
-
await flushLoopMicrotasks();
|
|
5121
|
-
expect(captured).toEqual([{ debounceMs: 15_000, maxWaitMs: 120_000 }]);
|
|
5122
|
-
triggerShutdown();
|
|
5123
|
-
await loop;
|
|
5124
|
-
});
|
|
5125
|
-
it("HQ_SYNC_EVENT_DEBOUNCE_MS / HQ_SYNC_EVENT_MAX_WAIT_MS override the watcher debounce", async () => {
|
|
5126
|
-
const savedDebounce = process.env.HQ_SYNC_EVENT_DEBOUNCE_MS;
|
|
5127
|
-
const savedMaxWait = process.env.HQ_SYNC_EVENT_MAX_WAIT_MS;
|
|
5128
|
-
process.env.HQ_SYNC_EVENT_DEBOUNCE_MS = "5000";
|
|
5129
|
-
process.env.HQ_SYNC_EVENT_MAX_WAIT_MS = "45000";
|
|
5130
|
-
try {
|
|
5131
|
-
const captured = [];
|
|
5132
|
-
const watcher = makeWatcherStub();
|
|
5133
|
-
const sleep = makeSteppableSleep();
|
|
5134
|
-
let triggerShutdown = () => { };
|
|
5135
|
-
const runPass = vi.fn().mockResolvedValue(completedPassOutcome());
|
|
5136
|
-
const loop = runRunnerWithLoop(watchArgv, {
|
|
5137
|
-
runPass,
|
|
5138
|
-
clock: new FakeClock(),
|
|
5139
|
-
createWatcher: (opts) => {
|
|
5140
|
-
captured.push({
|
|
5141
|
-
debounceMs: opts.debounceMs,
|
|
5142
|
-
maxWaitMs: opts.maxWaitMs,
|
|
5143
|
-
});
|
|
5144
|
-
return watcher;
|
|
5145
|
-
},
|
|
5146
|
-
sleep: sleep.sleep,
|
|
5147
|
-
onShutdownSignal: (handler) => {
|
|
5148
|
-
triggerShutdown = handler;
|
|
5149
|
-
return () => { };
|
|
5150
|
-
},
|
|
5151
|
-
});
|
|
5152
|
-
await flushLoopMicrotasks();
|
|
5153
|
-
expect(captured).toEqual([{ debounceMs: 5000, maxWaitMs: 45_000 }]);
|
|
5154
|
-
triggerShutdown();
|
|
5155
|
-
await loop;
|
|
5156
|
-
}
|
|
5157
|
-
finally {
|
|
5158
|
-
if (savedDebounce === undefined)
|
|
5159
|
-
delete process.env.HQ_SYNC_EVENT_DEBOUNCE_MS;
|
|
5160
|
-
else
|
|
5161
|
-
process.env.HQ_SYNC_EVENT_DEBOUNCE_MS = savedDebounce;
|
|
5162
|
-
if (savedMaxWait === undefined)
|
|
5163
|
-
delete process.env.HQ_SYNC_EVENT_MAX_WAIT_MS;
|
|
5164
|
-
else
|
|
5165
|
-
process.env.HQ_SYNC_EVENT_MAX_WAIT_MS = savedMaxWait;
|
|
5166
|
-
}
|
|
5167
|
-
});
|
|
5168
4831
|
});
|
|
5169
4832
|
// ---------------------------------------------------------------------------
|
|
5170
4833
|
// Re-initialize for each test (mock state hygiene)
|
|
@@ -6387,17 +6050,7 @@ describe("runRunnerWithLoop — Phase 3 event-sync wiring (US-017/018/019)", ()
|
|
|
6387
6050
|
"--hq-root",
|
|
6388
6051
|
"/tmp/hq",
|
|
6389
6052
|
],
|
|
6390
|
-
|
|
6391
|
-
[
|
|
6392
|
-
"--company",
|
|
6393
|
-
"acme",
|
|
6394
|
-
"--direction",
|
|
6395
|
-
"pull",
|
|
6396
|
-
"--scope-path",
|
|
6397
|
-
"remote.md",
|
|
6398
|
-
"--hq-root",
|
|
6399
|
-
"/tmp/hq",
|
|
6400
|
-
],
|
|
6053
|
+
["--company", "acme", "--direction", "pull", "--hq-root", "/tmp/hq"],
|
|
6401
6054
|
]));
|
|
6402
6055
|
const pushedRoutes = new Set();
|
|
6403
6056
|
for (const passArgv of passArgvs) {
|
|
@@ -6498,7 +6151,7 @@ describe("runRunnerWithLoop — Phase 3 event-sync wiring (US-017/018/019)", ()
|
|
|
6498
6151
|
// the differential signal for "did telemetry run".
|
|
6499
6152
|
// ---------------------------------------------------------------------------
|
|
6500
6153
|
describe("defaultCollectTelemetry person-entity gate", () => {
|
|
6501
|
-
function makeTelemetryClient(persons
|
|
6154
|
+
function makeTelemetryClient(persons) {
|
|
6502
6155
|
const calls = {
|
|
6503
6156
|
listByType: 0,
|
|
6504
6157
|
getTelemetryOptIn: 0,
|
|
@@ -6508,9 +6161,9 @@ describe("defaultCollectTelemetry person-entity gate", () => {
|
|
|
6508
6161
|
const client = {
|
|
6509
6162
|
entity: {
|
|
6510
6163
|
get: async () => ({}),
|
|
6511
|
-
listByType: async (
|
|
6164
|
+
listByType: async (_type) => {
|
|
6512
6165
|
calls.listByType++;
|
|
6513
|
-
return
|
|
6166
|
+
return persons;
|
|
6514
6167
|
},
|
|
6515
6168
|
},
|
|
6516
6169
|
// Telemetry surface (cast-to internally by defaultCollectTelemetry). With
|
|
@@ -6545,7 +6198,7 @@ describe("defaultCollectTelemetry person-entity gate", () => {
|
|
|
6545
6198
|
try {
|
|
6546
6199
|
const { client, calls } = makeTelemetryClient([]); // unprovisioned caller
|
|
6547
6200
|
await defaultCollectTelemetry(client, false, hqRoot);
|
|
6548
|
-
expect(calls.listByType).toBe(
|
|
6201
|
+
expect(calls.listByType).toBe(1); // orphan-safe probe ran
|
|
6549
6202
|
expect(calls.getTelemetryOptIn).toBe(0); // collectors never ran
|
|
6550
6203
|
expect(calls.postUsage).toBe(0);
|
|
6551
6204
|
expect(calls.postSkillInvocations).toBe(0);
|
|
@@ -6567,79 +6220,6 @@ describe("defaultCollectTelemetry person-entity gate", () => {
|
|
|
6567
6220
|
fs.rmSync(hqRoot, { recursive: true, force: true });
|
|
6568
6221
|
}
|
|
6569
6222
|
});
|
|
6570
|
-
it("runs telemetry for an agent machine identity with no person entity", async () => {
|
|
6571
|
-
const hqRoot = fs.mkdtempSync(path.join(os.tmpdir(), "hq-tele-agent-"));
|
|
6572
|
-
try {
|
|
6573
|
-
const AGENT = {
|
|
6574
|
-
uid: "agt_01KUSAGEAGENTTEST00000000",
|
|
6575
|
-
type: "agent",
|
|
6576
|
-
slug: "jeff",
|
|
6577
|
-
status: "active",
|
|
6578
|
-
bucketName: "hq-vault-agt_01KUSAGEAGENTTEST00000000",
|
|
6579
|
-
createdAt: "2026-06-01T00:00:00Z",
|
|
6580
|
-
};
|
|
6581
|
-
const { client, calls } = makeTelemetryClient([], [AGENT]);
|
|
6582
|
-
await defaultCollectTelemetry(client, false, hqRoot, undefined, undefined, AGENT.uid);
|
|
6583
|
-
expect(calls.listByType).toBe(2);
|
|
6584
|
-
// Both collectors forceCollect — neither probes GET /v1/usage/opt-in.
|
|
6585
|
-
expect(calls.getTelemetryOptIn).toBe(0);
|
|
6586
|
-
// Passed the gate: identity resolution writes machine-id before collecting.
|
|
6587
|
-
expect(fs.existsSync(path.join(hqRoot, ".hq", "machine-id"))).toBe(true);
|
|
6588
|
-
}
|
|
6589
|
-
finally {
|
|
6590
|
-
fs.rmSync(hqRoot, { recursive: true, force: true });
|
|
6591
|
-
}
|
|
6592
|
-
});
|
|
6593
|
-
it("does not force-collect when a claimed agent uid is absent from the self-listing", async () => {
|
|
6594
|
-
const hqRoot = fs.mkdtempSync(path.join(os.tmpdir(), "hq-tele-agent-miss-"));
|
|
6595
|
-
try {
|
|
6596
|
-
const AGENT = {
|
|
6597
|
-
uid: "agt_01KUSAGEAGENTTEST00000000",
|
|
6598
|
-
type: "agent",
|
|
6599
|
-
slug: "jeff",
|
|
6600
|
-
status: "active",
|
|
6601
|
-
bucketName: "hq-vault-agt_01KUSAGEAGENTTEST00000000",
|
|
6602
|
-
createdAt: "2026-06-01T00:00:00Z",
|
|
6603
|
-
};
|
|
6604
|
-
const { client, calls } = makeTelemetryClient([], [AGENT]);
|
|
6605
|
-
await defaultCollectTelemetry(client, false, hqRoot, undefined, undefined, "agt_01KSOMEONEELSE00000000000");
|
|
6606
|
-
expect(calls.listByType).toBe(2);
|
|
6607
|
-
expect(calls.getTelemetryOptIn).toBe(0);
|
|
6608
|
-
expect(fs.existsSync(path.join(hqRoot, ".hq", "machine-id"))).toBe(false);
|
|
6609
|
-
}
|
|
6610
|
-
finally {
|
|
6611
|
-
fs.rmSync(hqRoot, { recursive: true, force: true });
|
|
6612
|
-
}
|
|
6613
|
-
});
|
|
6614
|
-
it("does not treat any agt_ listing as self when more than one agent is visible", async () => {
|
|
6615
|
-
const hqRoot = fs.mkdtempSync(path.join(os.tmpdir(), "hq-tele-agent-many-"));
|
|
6616
|
-
try {
|
|
6617
|
-
const a = {
|
|
6618
|
-
uid: "agt_01KUSAGEAGENTTEST00000000",
|
|
6619
|
-
type: "agent",
|
|
6620
|
-
slug: "jeff",
|
|
6621
|
-
status: "active",
|
|
6622
|
-
bucketName: "hq-vault-agt_01KUSAGEAGENTTEST00000000",
|
|
6623
|
-
createdAt: "2026-06-01T00:00:00Z",
|
|
6624
|
-
};
|
|
6625
|
-
const b = {
|
|
6626
|
-
uid: "agt_01KUSAGEAGENTOTHER0000000",
|
|
6627
|
-
type: "agent",
|
|
6628
|
-
slug: "cx",
|
|
6629
|
-
status: "active",
|
|
6630
|
-
bucketName: "hq-vault-agt_01KUSAGEAGENTOTHER0000000",
|
|
6631
|
-
createdAt: "2026-06-02T00:00:00Z",
|
|
6632
|
-
};
|
|
6633
|
-
const { client, calls } = makeTelemetryClient([], [a, b]);
|
|
6634
|
-
await defaultCollectTelemetry(client, false, hqRoot);
|
|
6635
|
-
expect(calls.listByType).toBe(2);
|
|
6636
|
-
expect(calls.getTelemetryOptIn).toBe(0);
|
|
6637
|
-
expect(fs.existsSync(path.join(hqRoot, ".hq", "machine-id"))).toBe(false);
|
|
6638
|
-
}
|
|
6639
|
-
finally {
|
|
6640
|
-
fs.rmSync(hqRoot, { recursive: true, force: true });
|
|
6641
|
-
}
|
|
6642
|
-
});
|
|
6643
6223
|
it("fails open: a thrown person probe still lets telemetry run (no provisioned-user regression)", async () => {
|
|
6644
6224
|
const hqRoot = fs.mkdtempSync(path.join(os.tmpdir(), "hq-tele-throw-"));
|
|
6645
6225
|
try {
|