@indigoai-us/hq-cloud 6.18.14-alpha.2 → 6.18.15

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 (60) hide show
  1. package/dist/bin/sync-runner-receiver-unresolved-company.test.d.ts +2 -0
  2. package/dist/bin/sync-runner-receiver-unresolved-company.test.d.ts.map +1 -0
  3. package/dist/bin/sync-runner-receiver-unresolved-company.test.js +379 -0
  4. package/dist/bin/sync-runner-receiver-unresolved-company.test.js.map +1 -0
  5. package/dist/bin/sync-runner-watch-loop.d.ts +2 -1
  6. package/dist/bin/sync-runner-watch-loop.d.ts.map +1 -1
  7. package/dist/bin/sync-runner-watch-loop.js +76 -50
  8. package/dist/bin/sync-runner-watch-loop.js.map +1 -1
  9. package/dist/bin/sync-runner-watch-loop.test.js +2 -199
  10. package/dist/bin/sync-runner-watch-loop.test.js.map +1 -1
  11. package/dist/bin/sync-runner.d.ts +1 -12
  12. package/dist/bin/sync-runner.d.ts.map +1 -1
  13. package/dist/bin/sync-runner.js +14 -32
  14. package/dist/bin/sync-runner.js.map +1 -1
  15. package/dist/bin/sync-runner.test.js +4 -4
  16. package/dist/bin/sync-runner.test.js.map +1 -1
  17. package/dist/cli/rescue-classify-ordering.test.js +16 -10
  18. package/dist/cli/rescue-classify-ordering.test.js.map +1 -1
  19. package/dist/cli/rescue-core.d.ts.map +1 -1
  20. package/dist/cli/rescue-core.js +39 -5
  21. package/dist/cli/rescue-core.js.map +1 -1
  22. package/dist/cli/rescue-unchanged-delete.test.d.ts +2 -0
  23. package/dist/cli/rescue-unchanged-delete.test.d.ts.map +1 -0
  24. package/dist/cli/rescue-unchanged-delete.test.js +291 -0
  25. package/dist/cli/rescue-unchanged-delete.test.js.map +1 -0
  26. package/dist/cli/sync.d.ts.map +1 -1
  27. package/dist/cli/sync.js +22 -18
  28. package/dist/cli/sync.js.map +1 -1
  29. package/dist/cli/sync.test.js +32 -44
  30. package/dist/cli/sync.test.js.map +1 -1
  31. package/dist/object-io.d.ts.map +1 -1
  32. package/dist/object-io.js +10 -0
  33. package/dist/object-io.js.map +1 -1
  34. package/dist/object-io.test.js +28 -1
  35. package/dist/object-io.test.js.map +1 -1
  36. package/dist/qmd-reindex.d.ts +10 -30
  37. package/dist/qmd-reindex.d.ts.map +1 -1
  38. package/dist/qmd-reindex.js +41 -148
  39. package/dist/qmd-reindex.js.map +1 -1
  40. package/dist/qmd-reindex.test.js +77 -110
  41. package/dist/qmd-reindex.test.js.map +1 -1
  42. package/dist/sync/push-receiver.d.ts +10 -14
  43. package/dist/sync/push-receiver.d.ts.map +1 -1
  44. package/dist/sync/push-receiver.js +40 -71
  45. package/dist/sync/push-receiver.js.map +1 -1
  46. package/dist/sync/push-receiver.test.js +1 -84
  47. package/dist/sync/push-receiver.test.js.map +1 -1
  48. package/dist/sync-core.d.ts +7 -0
  49. package/dist/sync-core.d.ts.map +1 -1
  50. package/dist/sync-core.js +12 -0
  51. package/dist/sync-core.js.map +1 -1
  52. package/package.json +1 -1
  53. package/dist/sync/targeted-pull-prefixes.d.ts +0 -9
  54. package/dist/sync/targeted-pull-prefixes.d.ts.map +0 -1
  55. package/dist/sync/targeted-pull-prefixes.js +0 -43
  56. package/dist/sync/targeted-pull-prefixes.js.map +0 -1
  57. package/dist/sync/targeted-pull-prefixes.test.d.ts +0 -2
  58. package/dist/sync/targeted-pull-prefixes.test.d.ts.map +0 -1
  59. package/dist/sync/targeted-pull-prefixes.test.js +0 -33
  60. package/dist/sync/targeted-pull-prefixes.test.js.map +0 -1
@@ -35,6 +35,7 @@ import { InlineSyncWorkerAdapter, } from "../sync/sync-work.js";
35
35
  import { buildRoutePullArgv, buildScopedPushArgv, buildScopedDrainPullArgv, buildTargetedPullArgv, isWatchRouteSelected, routeChangeToTarget, routeKey, scopePathForRoute, } from "./sync-runner-watch-routes.js";
36
36
  import { resolveSkipCompanies } from "./sync-runner-planning.js";
37
37
  const FULL_RECONCILE_EVERY_TICKS = 10;
38
+ const warnedUnresolvedReceiverCompanies = new Set();
38
39
  /** Maximum time shutdown waits for event-sync startup before abandoning it. */
39
40
  export const EVENT_SYNC_STARTUP_SHUTDOWN_WAIT_MS = 5_000;
40
41
  /** A deferred full chore must get the next admission after this long. */
@@ -1318,6 +1319,17 @@ function isBulkIntentOverride() {
1318
1319
  function passExitCode(outcome) {
1319
1320
  return typeof outcome === "number" ? outcome : outcome.exitCode;
1320
1321
  }
1322
+ export function shouldAcknowledgeParallelUnresolvedCompany(accepted, exitCode) {
1323
+ return accepted && exitCode === UNRESOLVED_COMPANY_EXIT;
1324
+ }
1325
+ function unresolvedReceiverMessagesWereRedelivered(events, receiveCountsByPath) {
1326
+ return events.every((event) => {
1327
+ const receiveCounts = receiveCountsByPath?.get(event.relativePath);
1328
+ return receiveCounts !== undefined &&
1329
+ receiveCounts.length > 0 &&
1330
+ receiveCounts.every((count) => count !== undefined && Number.isFinite(count) && count >= 2);
1331
+ });
1332
+ }
1321
1333
  function targetedReceiverDispatchError(outcome, accepted, authRequiredExitCode) {
1322
1334
  const exitCode = passExitCode(outcome);
1323
1335
  const result = typeof outcome === "number" ? undefined : outcome.result;
@@ -1376,18 +1388,8 @@ export async function runWatchLoop(argv, parsed, deps, runtime) {
1376
1388
  ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
1377
1389
  const stdout = deps.stdout ?? process.stdout;
1378
1390
  const idleHeartbeatIntervalMs = deps.idleHeartbeatIntervalMs ?? WATCH_IDLE_HEARTBEAT_INTERVAL_MS;
1379
- const writeMaintenanceProgress = () => {
1380
- stdout.write(JSON.stringify({
1381
- type: "maintenance-progress",
1382
- company: "watch",
1383
- bytesProcessed: 0,
1384
- totalBytes: 0,
1385
- }) + "\n");
1386
- };
1387
1391
  const monotonicNow = deps.monotonicNow ?? (() => performance.now());
1388
1392
  const runPass = deps.runPass ?? runtime.runPassWithOperationLockAlreadyHeld;
1389
- const qmdShutdownController = new AbortController();
1390
- const runPassSupportsQmdLifecycle = deps.runPass === undefined || deps.runPassSupportsQmdLifecycle === true;
1391
1393
  const runPassSupportsCooperativeCheckpoint = deps.runPass === undefined || deps.runPassSupportsCooperativeCheckpoint === true;
1392
1394
  const pollIdx = argv.indexOf("--poll-remote-ms");
1393
1395
  // An explicit --poll-remote-ms pins a fixed interval (menubar/CLI still set
@@ -1595,20 +1597,13 @@ export async function runWatchLoop(argv, parsed, deps, runtime) {
1595
1597
  }
1596
1598
  };
1597
1599
  const invokePass = () => {
1598
- if (!runPassSupportsCooperativeCheckpoint) {
1599
- if (!runPassSupportsQmdLifecycle)
1600
- return runPass(passArgvForRun);
1601
- return runPass(passArgvForRun, undefined, undefined, undefined, undefined, qmdShutdownController.signal, writeMaintenanceProgress, idleHeartbeatIntervalMs);
1602
- }
1600
+ if (!runPassSupportsCooperativeCheckpoint)
1601
+ return runPass(passArgvForRun);
1603
1602
  // This callback is emitted by the production runner after journal
1604
1603
  // repair/seed and before fanout. It starts ingress early, but never
1605
1604
  // admits a second durable writer: queued realtime still waits for the
1606
1605
  // existing scheduler's cooperative hand-off checkpoint.
1607
- const onJournalsReady = () => activateEventPushSurfaces();
1608
- if (!runPassSupportsQmdLifecycle) {
1609
- return runPass(passArgvForRun, checkpoint, onJournalsReady, recordDurableAppliedUpserts, workClass);
1610
- }
1611
- return runPass(passArgvForRun, checkpoint, onJournalsReady, recordDurableAppliedUpserts, workClass, qmdShutdownController.signal, writeMaintenanceProgress, idleHeartbeatIntervalMs);
1606
+ return runPass(passArgvForRun, checkpoint, () => activateEventPushSurfaces(), recordDurableAppliedUpserts, workClass);
1612
1607
  };
1613
1608
  // Observe the heap at pass boundaries — a gated checkpoint immediately before
1614
1609
  // admitting a pass and immediately after it returns. A pass that runs a long
@@ -1801,14 +1796,18 @@ export async function runWatchLoop(argv, parsed, deps, runtime) {
1801
1796
  resolveInterruptWait = null;
1802
1797
  resolve?.();
1803
1798
  };
1804
- let reportWatcherLiveness = () => undefined;
1805
1799
  const waitForNextPass = async (delayMs) => {
1806
1800
  if (interruptGeneration !== observedInterruptGeneration) {
1807
1801
  observedInterruptGeneration = interruptGeneration;
1808
1802
  return;
1809
1803
  }
1810
1804
  const heartbeatTimer = setInterval(() => {
1811
- writeMaintenanceProgress();
1805
+ stdout.write(JSON.stringify({
1806
+ type: "maintenance-progress",
1807
+ company: "watch",
1808
+ bytesProcessed: 0,
1809
+ totalBytes: 0,
1810
+ }) + "\n");
1812
1811
  reportWatcherLiveness();
1813
1812
  }, idleHeartbeatIntervalMs);
1814
1813
  heartbeatTimer.unref?.();
@@ -2109,7 +2108,7 @@ export async function runWatchLoop(argv, parsed, deps, runtime) {
2109
2108
  * liveness heartbeat as well so an operator can distinguish a live watcher
2110
2109
  * from a long-running poll-only process without inspecting /proc.
2111
2110
  */
2112
- reportWatcherLiveness = () => {
2111
+ const reportWatcherLiveness = () => {
2113
2112
  if (!eventPush || stopped)
2114
2113
  return;
2115
2114
  reattemptWatcherDiscoveryIfDue();
@@ -3308,6 +3307,18 @@ export async function runWatchLoop(argv, parsed, deps, runtime) {
3308
3307
  };
3309
3308
  let receiver = null;
3310
3309
  let receiverHasQueuedWork = false;
3310
+ const warnUnresolvedReceiverCompany = (route, pathCount) => {
3311
+ if (route.kind !== "company" ||
3312
+ warnedUnresolvedReceiverCompanies.has(route.slug))
3313
+ return;
3314
+ warnedUnresolvedReceiverCompanies.add(route.slug);
3315
+ (deps.stderr ?? process.stderr).write(`${JSON.stringify({
3316
+ level: "warn",
3317
+ event: "receiver.unresolved-company",
3318
+ companySlug: route.slug,
3319
+ pathCount,
3320
+ })}\n`);
3321
+ };
3311
3322
  const onReceiveActivity = (hasMessages) => {
3312
3323
  receiverHasQueuedWork = hasMessages;
3313
3324
  if (hasMessages)
@@ -3555,7 +3566,7 @@ export async function runWatchLoop(argv, parsed, deps, runtime) {
3555
3566
  // a known-small event can be slow solely because an earlier event exhausted
3556
3567
  // the delivery-wide fast budget.
3557
3568
  const receiverLaneByBatch = new WeakMap();
3558
- const drainParallelReceiverLane = async (lane, laneEvents) => {
3569
+ const drainParallelReceiverLane = async (lane, laneEvents, receiveCountsByPath) => {
3559
3570
  const eventsByRoute = new Map();
3560
3571
  for (const event of laneEvents) {
3561
3572
  const route = routeChangeToTarget(event.relativePath);
@@ -3569,20 +3580,31 @@ export async function runWatchLoop(argv, parsed, deps, runtime) {
3569
3580
  // Submit every route in this lane before awaiting any result. In
3570
3581
  // particular, a later route's fast receipt must never wait for an
3571
3582
  // earlier route's slow pull to finish.
3572
- const completed = await Promise.all([...eventsByRoute.values()].map(({ route, events }) => {
3583
+ const routeGroups = [...eventsByRoute.values()];
3584
+ const completed = await Promise.all(routeGroups.map(({ route, events }) => {
3573
3585
  return runRealtimePass("receiver", "pull", events.map((event) => event.relativePath), lane, (slice, telemetry, coordinatorFenced) => {
3574
3586
  const slicePaths = slice.paths.map((path) => path.canonicalPath);
3575
3587
  const exactFileUpserts = exactUpsertPathsForCoordinatorSlice(events, slicePaths);
3576
3588
  return withScopeContentHashesFile(route, events, slicePaths, (contentHashesFile) => runGuarded(buildTargetedPullArgv(route, passArgv, slicePaths, exactFileUpserts, contentHashesFile), undefined, lane, "realtime", lane === "fast", slice.signal, false, telemetry, coordinatorFenced));
3577
3589
  });
3578
3590
  }));
3579
- for (const execution of completed) {
3580
- if (!execution.accepted || passExitCode(execution.result) !== 0) {
3591
+ for (const [index, execution] of completed.entries()) {
3592
+ const { route, events } = routeGroups[index];
3593
+ const pathCount = events.length;
3594
+ const exitCode = passExitCode(execution.result);
3595
+ if (shouldAcknowledgeParallelUnresolvedCompany(execution.accepted, exitCode)) {
3596
+ if (!unresolvedReceiverMessagesWereRedelivered(events, receiveCountsByPath)) {
3597
+ throw targetedReceiverDispatchError(execution.result, execution.accepted, runtime.authRequiredPassExit);
3598
+ }
3599
+ warnUnresolvedReceiverCompany(route, pathCount);
3600
+ continue;
3601
+ }
3602
+ if (!execution.accepted || exitCode !== 0) {
3581
3603
  throw targetedReceiverDispatchError(execution.result, execution.accepted, runtime.authRequiredPassExit);
3582
3604
  }
3583
3605
  }
3584
3606
  };
3585
- const receiverSyncBatchFn = async ({ events }) => {
3607
+ const receiverSyncBatchFn = async ({ events, approximateReceiveCountsByPath, }) => {
3586
3608
  if (stopped)
3587
3609
  return;
3588
3610
  // The injectable receiver seam and the production SQS receiver both
@@ -3600,19 +3622,19 @@ export async function runWatchLoop(argv, parsed, deps, runtime) {
3600
3622
  const lane = receiverLaneByBatch.get(events);
3601
3623
  if (lane) {
3602
3624
  receiverLaneByBatch.delete(events);
3603
- await drainParallelReceiverLane(lane, events);
3625
+ await drainParallelReceiverLane(lane, events, approximateReceiveCountsByPath);
3604
3626
  return;
3605
3627
  }
3606
3628
  // Direct embedders may invoke the engine without first going through
3607
3629
  // SqsPushReceiver. Preserve that seam while applying the same
3608
3630
  // delivery-wide partition the receiver would expose.
3609
3631
  const lanes = partitionByKnownSizes(events, receiverEventSize);
3610
- await drainParallelReceiverLane("fast", lanes.fast);
3611
- await drainParallelReceiverLane("slow", lanes.slow);
3632
+ await drainParallelReceiverLane("fast", lanes.fast, approximateReceiveCountsByPath);
3633
+ await drainParallelReceiverLane("slow", lanes.slow, approximateReceiveCountsByPath);
3612
3634
  return;
3613
3635
  }
3614
3636
  const lanes = partitionByKnownSizes(events, receiverEventSize);
3615
- const drainLane = async (lane, laneEvents) => {
3637
+ const drainLane = async (lane, laneEvents, receiveCountsByPath) => {
3616
3638
  const eventsByRoute = new Map();
3617
3639
  for (const event of laneEvents) {
3618
3640
  const route = routeChangeToTarget(event.relativePath);
@@ -3632,7 +3654,15 @@ export async function runWatchLoop(argv, parsed, deps, runtime) {
3632
3654
  for (const { route, events: routeEvents, upsertPaths } of eventsByRoute.values()) {
3633
3655
  const paths = routeEvents.map((event) => event.relativePath);
3634
3656
  const result = await withScopeContentHashesFile(route, routeEvents, paths, (contentHashesFile) => runGuarded(buildTargetedPullArgv(route, passArgv, paths, upsertPaths, contentHashesFile), undefined, lane, "realtime"));
3635
- if (passExitCode(result) !== 0) {
3657
+ const exitCode = passExitCode(result);
3658
+ if (exitCode === UNRESOLVED_COMPANY_EXIT) {
3659
+ if (!unresolvedReceiverMessagesWereRedelivered(routeEvents, receiveCountsByPath)) {
3660
+ throw targetedReceiverDispatchError(result, true, runtime.authRequiredPassExit);
3661
+ }
3662
+ warnUnresolvedReceiverCompany(route, routeEvents.length);
3663
+ continue;
3664
+ }
3665
+ if (exitCode !== 0) {
3636
3666
  throw targetedReceiverDispatchError(result, true, runtime.authRequiredPassExit);
3637
3667
  }
3638
3668
  }
@@ -3641,23 +3671,21 @@ export async function runWatchLoop(argv, parsed, deps, runtime) {
3641
3671
  // established slow pass continue while the small scoped pull uses its
3642
3672
  // ref-counted lock carve-out; the residue remains serialized and cannot
3643
3673
  // starve the slow lane.
3644
- await drainLane("fast", lanes.fast);
3645
- await drainLane("slow", lanes.slow);
3674
+ await drainLane("fast", lanes.fast, approximateReceiveCountsByPath);
3675
+ await drainLane("slow", lanes.slow, approximateReceiveCountsByPath);
3646
3676
  };
3647
- receiverSyncBatchFn.partition = (events, maxPaths) => {
3677
+ receiverSyncBatchFn.partition = (events) => {
3648
3678
  const lanes = partitionByKnownSizes(events, receiverEventSize);
3649
- const partitionMaxPaths = Math.max(1, maxPaths ?? events.length);
3650
- return ["fast", "slow"].flatMap((lane) => {
3651
- const laneEvents = lanes[lane];
3652
- const chunks = [];
3653
- for (let offset = 0; offset < laneEvents.length; offset += partitionMaxPaths) {
3654
- const chunk = laneEvents.slice(offset, offset + partitionMaxPaths);
3655
- if (parallelCoordinatorEnabled)
3656
- receiverLaneByBatch.set(chunk, lane);
3657
- chunks.push(chunk);
3658
- }
3659
- return chunks;
3660
- });
3679
+ if (parallelCoordinatorEnabled) {
3680
+ return ["fast", "slow"].flatMap((lane) => {
3681
+ const laneEvents = lanes[lane];
3682
+ if (laneEvents.length === 0)
3683
+ return [];
3684
+ receiverLaneByBatch.set(laneEvents, lane);
3685
+ return [laneEvents];
3686
+ });
3687
+ }
3688
+ return [lanes.fast, lanes.slow].filter((lane) => lane.length > 0);
3661
3689
  };
3662
3690
  const createReceiver = deps.createReceiver ?? (() => new NoopPushReceiver());
3663
3691
  receiver = createReceiver({
@@ -3784,7 +3812,6 @@ export async function runWatchLoop(argv, parsed, deps, runtime) {
3784
3812
  if (stopped)
3785
3813
  return shutdownPromise ?? Promise.resolve();
3786
3814
  stopped = true;
3787
- qmdShutdownController.abort();
3788
3815
  eventSyncShutdownDeadlineMs =
3789
3816
  Date.now() + EVENT_SYNC_STARTUP_SHUTDOWN_WAIT_MS;
3790
3817
  resolveStoppedQueue();
@@ -3977,7 +4004,6 @@ export async function runWatchLoop(argv, parsed, deps, runtime) {
3977
4004
  if (recycleRequested !== null)
3978
4005
  return;
3979
4006
  recycleRequested = record;
3980
- qmdShutdownController.abort();
3981
4007
  // Additive, non-error NDJSON protocol event for the desktop supervisor.
3982
4008
  stdout.write(`${JSON.stringify({ type: "heap-recycle", ...record })}\n`);
3983
4009
  // Compact numeric line, worded to match none of hq-desktop-app's fatal